package br.gov.sigefp.treasury.service;

import br.gov.sigefp.common.exception.ResourceNotFoundException;
import br.gov.sigefp.treasury.api.dto.CashFlowDTO;
import br.gov.sigefp.treasury.api.dto.CreateCashFlowDTO;
import br.gov.sigefp.treasury.domain.CashAccount;
import br.gov.sigefp.treasury.domain.CashFlow;
import br.gov.sigefp.treasury.repository.CashAccountRepository;
import br.gov.sigefp.treasury.repository.CashFlowRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Serviço de aplicação para gestão de fluxo de caixa.
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class CashFlowService {

    private final CashFlowRepository cashFlowRepository;
    private final CashAccountRepository cashAccountRepository;
    private final CashAccountService cashAccountService;

    public CashFlowDTO registerFlow(CreateCashFlowDTO dto) {
        CashAccount cashAccount = cashAccountRepository.findById(dto.getCashAccountId())
                .orElseThrow(() -> new ResourceNotFoundException(
                    "Conta não encontrada: " + dto.getCashAccountId()));

        // Calcular saldo após a transação
        BigDecimal currentBalance = cashAccount.getCurrentBalance();
        BigDecimal balanceAfter;
        
        if ("INFLOW".equals(dto.getType())) {
            balanceAfter = currentBalance.add(dto.getAmount());
        } else {
            balanceAfter = currentBalance.subtract(dto.getAmount());
        }

        CashFlow cashFlow = CashFlow.builder()
                .cashAccount(cashAccount)
                .transactionDate(dto.getTransactionDate())
                .type(dto.getType())
                .amount(dto.getAmount())
                .description(dto.getDescription())
                .referenceId(dto.getReferenceId())
                .referenceType(dto.getReferenceType())
                .balanceAfter(balanceAfter)
                .build();

        CashFlow saved = cashFlowRepository.save(cashFlow);

        // Atualizar saldo da conta
        cashAccountService.updateBalance(dto.getCashAccountId(), dto.getAmount(), dto.getType());

        log.info("Fluxo de caixa registrado: id={}, accountId={}, type={}, amount={}", 
                saved.getId(), dto.getCashAccountId(), dto.getType(), dto.getAmount());

        return toDTO(saved);
    }

    @Transactional(readOnly = true)
    public CashFlowDTO findById(UUID id) {
        CashFlow cashFlow = cashFlowRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Fluxo não encontrado: " + id));
        return toDTO(cashFlow);
    }

    @Transactional(readOnly = true)
    public Page<CashFlowDTO> findByCashAccountId(UUID cashAccountId, Pageable pageable) {
        return cashFlowRepository.findByCashAccountId(cashAccountId, pageable)
                .map(this::toDTO);
    }

    @Transactional(readOnly = true)
    public Page<CashFlowDTO> findByCashAccountIdAndDateRange(
            UUID cashAccountId, LocalDate startDate, LocalDate endDate, Pageable pageable) {
        return cashFlowRepository.findByCashAccountIdAndDateRange(
                cashAccountId, startDate, endDate, pageable)
                .map(this::toDTO);
    }

    @Transactional(readOnly = true)
    public BigDecimal calculateProjectedBalance(UUID cashAccountId, LocalDate targetDate) {
        CashAccount cashAccount = cashAccountRepository.findById(cashAccountId)
                .orElseThrow(() -> new ResourceNotFoundException("Conta não encontrada: " + cashAccountId));

        BigDecimal currentBalance = cashAccount.getCurrentBalance();
        LocalDate today = LocalDate.now();

        if (targetDate.isBefore(today)) {
            return currentBalance; // Não projeta para o passado
        }

        // Calcular projeção baseada em fluxos futuros programados
        // (assumindo que há uma forma de identificar fluxos programados)
        // Por enquanto, retorna o saldo atual
        return currentBalance;
    }

    @Transactional(readOnly = true)
    public Map<String, BigDecimal> getFlowSummary(UUID cashAccountId, LocalDate startDate, LocalDate endDate) {
        BigDecimal totalInflow = cashFlowRepository.calculateTotalByAccountAndType(
                cashAccountId, "INFLOW", startDate, endDate);
        BigDecimal totalOutflow = cashFlowRepository.calculateTotalByAccountAndType(
                cashAccountId, "OUTFLOW", startDate, endDate);

        if (totalInflow == null) totalInflow = BigDecimal.ZERO;
        if (totalOutflow == null) totalOutflow = BigDecimal.ZERO;

        return Map.of(
            "totalInflow", totalInflow,
            "totalOutflow", totalOutflow,
            "netFlow", totalInflow.subtract(totalOutflow)
        );
    }

    @Transactional(readOnly = true)
    public List<CashFlowDTO> findByReference(String referenceType, UUID referenceId) {
        return cashFlowRepository.findByReference(referenceType, referenceId).stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    private CashFlowDTO toDTO(CashFlow cashFlow) {
        return CashFlowDTO.builder()
                .id(cashFlow.getId())
                .cashAccountId(cashFlow.getCashAccount().getId())
                .transactionDate(cashFlow.getTransactionDate())
                .type(cashFlow.getType())
                .amount(cashFlow.getAmount())
                .description(cashFlow.getDescription())
                .referenceId(cashFlow.getReferenceId())
                .referenceType(cashFlow.getReferenceType())
                .balanceAfter(cashFlow.getBalanceAfter())
                .createdAt(cashFlow.getCreatedAt())
                .updatedAt(cashFlow.getUpdatedAt())
                .build();
    }
}

