package ru.soune.nocopy.service.tokenoperation;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.nocopy.entity.tokenoperation.OperationType;
import ru.soune.nocopy.entity.tokenoperation.TokenOperation;
import ru.soune.nocopy.repository.TokenOperationRepository;

import jakarta.persistence.EntityNotFoundException;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TokenOperationService {

    private final TokenOperationRepository repository;

    @Transactional
    public TokenOperation create(OperationType operationType, Long userId, Long spent) {
        TokenOperation tokenOperation = new TokenOperation();

        tokenOperation.setOperationType(operationType);
        tokenOperation.setUserId(userId);
        tokenOperation.setSpent(spent);
        tokenOperation.setCreatedAt(LocalDateTime.now());

        return  repository.save(tokenOperation);
    }

    public TokenOperation findById(Long id) {
        log.debug("Finding token operation by id: {}", id);
        return repository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("TokenOperation not found with id: " + id));
    }

    public Page<TokenOperation> findAll(int page, int size, String sortBy, String direction) {
        log.debug("Finding all operations with pagination: page={}, size={}, sortBy={}, dir={}",
                page, size, sortBy, direction);

        Sort sort = direction.equalsIgnoreCase("desc")
                ? Sort.by(sortBy).descending()
                : Sort.by(sortBy).ascending();

        Pageable pageable = PageRequest.of(page, size, sort);
        return repository.findAll(pageable);
    }

    public Page<TokenOperation> findByFilters(
            Long userId,
            OperationType operationType,
            Long minSpent,
            Long maxSpent,
            int page,
            int size,
            String sortBy,
            String direction) {

        log.debug("Finding by filters: userId={}, type={}, minSpent={}, maxSpent={}",
                userId, operationType, minSpent, maxSpent);

        Sort sort = Sort.by(Sort.Direction.fromString(direction), sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);

        return repository.findByFilters(userId, operationType, minSpent, maxSpent, pageable);
    }

    public Page<TokenOperation> findByUserId(Long userId, int page, int size) {
        log.debug("Finding operations for user id: {}", userId);
        Pageable pageable = PageRequest.of(page, size, Sort.by("id").descending());
        return repository.findByUserId(userId, pageable);
    }

    @Transactional
    public TokenOperation update(Long id, TokenOperation updatedOperation) {
        log.info("Updating token operation with id: {}", id);

        TokenOperation existing = findById(id);

        existing.setOperationType(updatedOperation.getOperationType());
        existing.setUserId(updatedOperation.getUserId());
        existing.setSpent(updatedOperation.getSpent());

        return repository.save(existing);
    }

    @Transactional
    public void deleteById(Long id) {
        log.info("Deleting token operation with id: {}", id);

        if (!repository.existsById(id)) {
            throw new EntityNotFoundException("TokenOperation not found with id: " + id);
        }
        repository.deleteById(id);
    }

    @Transactional
    public void deleteByUserId(Long userId) {
        log.warn("Deleting all operations for user id: {}", userId);
        List<TokenOperation> userOps = repository.findByUserId(userId);
        repository.deleteAll(userOps);
    }

    public Long getTotalSpentByUserAndType(Long userId, OperationType type) {
        Long sum = repository.sumSpentByUserAndType(userId, type);
        return sum != null ? sum : 0L;
    }

    public Long countByUserId(Long userId) {
        return repository.countByUserId(userId);
    }
}