package br.gov.sigefp.treasury.service;

import br.gov.sigefp.common.exception.BusinessException;
import br.gov.sigefp.common.exception.ResourceNotFoundException;
import br.gov.sigefp.treasury.api.dto.BankReconciliationDTO;
import br.gov.sigefp.treasury.api.dto.CreateBankReconciliationDTO;
import br.gov.sigefp.treasury.api.dto.ReconciliationItemDTO;
import br.gov.sigefp.treasury.domain.BankReconciliation;
import br.gov.sigefp.treasury.domain.CashAccount;
import br.gov.sigefp.treasury.domain.ReconciliationItem;
import br.gov.sigefp.treasury.repository.BankReconciliationRepository;
import br.gov.sigefp.treasury.repository.CashAccountRepository;
import br.gov.sigefp.treasury.repository.ReconciliationItemRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * Serviço de aplicação para gestão de conciliação bancária.
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class BankReconciliationService {

    private final BankReconciliationRepository reconciliationRepository;
    private final CashAccountRepository cashAccountRepository;
    private final ReconciliationItemRepository reconciliationItemRepository;
    private final CashAccountService cashAccountService;

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

        BigDecimal systemBalance = cashAccountService.getCurrentBalance(dto.getCashAccountId());
        BigDecimal difference = dto.getStatementBalance().subtract(systemBalance);

        BankReconciliation reconciliation = BankReconciliation.builder()
                .cashAccount(cashAccount)
                .reconciliationDate(dto.getReconciliationDate())
                .statementBalance(dto.getStatementBalance())
                .systemBalance(systemBalance)
                .difference(difference)
                .status("PENDING")
                .build();

        BankReconciliation saved = reconciliationRepository.save(reconciliation);

        // Criar itens de conciliação se fornecidos
        if (dto.getReconciliationItems() != null && !dto.getReconciliationItems().isEmpty()) {
            for (ReconciliationItemDTO itemDTO : dto.getReconciliationItems()) {
                ReconciliationItem item = ReconciliationItem.builder()
                        .reconciliation(saved)
                        .transactionDate(itemDTO.getTransactionDate())
                        .description(itemDTO.getDescription())
                        .statementAmount(itemDTO.getStatementAmount())
                        .systemAmount(itemDTO.getSystemAmount())
                        .matchStatus("PENDING")
                        .build();
                reconciliationItemRepository.save(item);
                saved.getReconciliationItems().add(item);
            }
        }

        log.info("Conciliação bancária criada: id={}, accountId={}, difference={}", 
                saved.getId(), dto.getCashAccountId(), difference);

        return toDTO(saved);
    }

    public BankReconciliationDTO reconcile(UUID reconciliationId) {
        BankReconciliation reconciliation = reconciliationRepository.findById(reconciliationId)
                .orElseThrow(() -> new ResourceNotFoundException(
                    "Conciliação não encontrada: " + reconciliationId));

        if (!"PENDING".equals(reconciliation.getStatus())) {
            throw new BusinessException(
                "Apenas conciliações pendentes podem ser reconciliadas",
                "INVALID_STATUS",
                org.springframework.http.HttpStatus.CONFLICT);
        }

        // Tentar matching automático
        performAutomaticMatching(reconciliation);

        // Verificar se todos os itens foram reconciliados
        long unmatchedCount = reconciliation.getReconciliationItems().stream()
                .filter(item -> !"MATCHED".equals(item.getMatchStatus()))
                .count();

        if (unmatchedCount == 0 && reconciliation.getDifference().compareTo(BigDecimal.ZERO) == 0) {
            reconciliation.setStatus("RECONCILED");
        } else if (reconciliation.getDifference().compareTo(BigDecimal.ZERO) != 0) {
            reconciliation.setStatus("DISCREPANCY");
        }

        BankReconciliation saved = reconciliationRepository.save(reconciliation);
        return toDTO(saved);
    }

    public void matchItem(UUID reconciliationId, UUID itemId, UUID systemTransactionId) {
        BankReconciliation reconciliation = reconciliationRepository.findById(reconciliationId)
                .orElseThrow(() -> new ResourceNotFoundException(
                    "Conciliação não encontrada: " + reconciliationId));

        ReconciliationItem item = reconciliationItemRepository.findById(itemId)
                .orElseThrow(() -> new ResourceNotFoundException(
                    "Item não encontrado: " + itemId));

        if (!item.getReconciliation().getId().equals(reconciliationId)) {
            throw new BusinessException(
                "Item não pertence a esta conciliação",
                "INVALID_ITEM",
                org.springframework.http.HttpStatus.BAD_REQUEST);
        }

        item.setMatchStatus("MATCHED");
        item.setMatchedTransactionId(systemTransactionId);
        reconciliationItemRepository.save(item);

        log.info("Item de conciliação correspondido: itemId={}, transactionId={}", 
                itemId, systemTransactionId);
    }

    public BankReconciliationDTO finalize(UUID reconciliationId, UUID reconciledBy) {
        BankReconciliation reconciliation = reconciliationRepository.findById(reconciliationId)
                .orElseThrow(() -> new ResourceNotFoundException(
                    "Conciliação não encontrada: " + reconciliationId));

        if (!"RECONCILED".equals(reconciliation.getStatus()) && 
            !"DISCREPANCY".equals(reconciliation.getStatus())) {
            throw new BusinessException(
                "Apenas conciliações reconciliadas ou com discrepância podem ser finalizadas",
                "INVALID_STATUS",
                org.springframework.http.HttpStatus.CONFLICT);
        }

        reconciliation.setStatus("RECONCILED");
        reconciliation.setReconciledBy(reconciledBy);
        reconciliation.setReconciledAt(LocalDateTime.now());

        // Atualizar saldo da conta se houver diferença ajustada
        if (reconciliation.getDifference().compareTo(BigDecimal.ZERO) != 0) {
            // Ajustar saldo do sistema para corresponder ao extrato
            cashAccountService.updateBalance(
                reconciliation.getCashAccount().getId(),
                reconciliation.getDifference(),
                reconciliation.getDifference().compareTo(BigDecimal.ZERO) > 0 ? "INFLOW" : "OUTFLOW");
        }

        BankReconciliation saved = reconciliationRepository.save(reconciliation);
        log.info("Conciliação finalizada: id={}, reconciledBy={}", reconciliationId, reconciledBy);

        return toDTO(saved);
    }

    @Transactional(readOnly = true)
    public BankReconciliationDTO findById(UUID id) {
        BankReconciliation reconciliation = reconciliationRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Conciliação não encontrada: " + id));
        return toDTO(reconciliation);
    }

    @Transactional(readOnly = true)
    public List<BankReconciliationDTO> findByCashAccountId(UUID cashAccountId) {
        return reconciliationRepository.findByCashAccountId(cashAccountId).stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public List<ReconciliationItemDTO> findUnmatchedItems(UUID reconciliationId) {
        BankReconciliation reconciliation = reconciliationRepository.findById(reconciliationId)
                .orElseThrow(() -> new ResourceNotFoundException(
                    "Conciliação não encontrada: " + reconciliationId));

        return reconciliation.getReconciliationItems().stream()
                .filter(item -> !"MATCHED".equals(item.getMatchStatus()))
                .map(this::itemToDTO)
                .collect(Collectors.toList());
    }

    /**
     * Realiza matching automático de itens baseado em data e valor.
     */
    private void performAutomaticMatching(BankReconciliation reconciliation) {
        // Implementação simplificada: matching por data e valor exato
        // Em produção, poderia usar algoritmos mais sofisticados
        for (ReconciliationItem item : reconciliation.getReconciliationItems()) {
            if ("PENDING".equals(item.getMatchStatus()) && item.getSystemAmount() != null) {
                // Verificar se há correspondência exata
                if (item.getStatementAmount() != null &&
                    item.getStatementAmount().compareTo(item.getSystemAmount()) == 0) {
                    item.setMatchStatus("MATCHED");
                    reconciliationItemRepository.save(item);
                }
            }
        }
    }

    private BankReconciliationDTO toDTO(BankReconciliation reconciliation) {
        List<ReconciliationItemDTO> items = reconciliation.getReconciliationItems().stream()
                .map(this::itemToDTO)
                .collect(Collectors.toList());

        return BankReconciliationDTO.builder()
                .id(reconciliation.getId())
                .cashAccountId(reconciliation.getCashAccount().getId())
                .reconciliationDate(reconciliation.getReconciliationDate())
                .statementBalance(reconciliation.getStatementBalance())
                .systemBalance(reconciliation.getSystemBalance())
                .difference(reconciliation.getDifference())
                .status(reconciliation.getStatus())
                .reconciledBy(reconciliation.getReconciledBy())
                .reconciledAt(reconciliation.getReconciledAt())
                .items(items)
                .createdAt(reconciliation.getCreatedAt())
                .updatedAt(reconciliation.getUpdatedAt())
                .build();
    }

    private ReconciliationItemDTO itemToDTO(ReconciliationItem item) {
        return ReconciliationItemDTO.builder()
                .id(item.getId())
                .reconciliationId(item.getReconciliation().getId())
                .transactionDate(item.getTransactionDate())
                .description(item.getDescription())
                .statementAmount(item.getStatementAmount())
                .systemAmount(item.getSystemAmount())
                .matchStatus(item.getMatchStatus())
                .matchedTransactionId(item.getMatchedTransactionId())
                .build();
    }
}

