package br.gov.sigefp.treasury.service;

import br.gov.sigefp.treasury.domain.CashAccount;
import br.gov.sigefp.treasury.domain.TreasuryEntry;
import br.gov.sigefp.treasury.domain.TreasuryEntryType;
import br.gov.sigefp.treasury.repository.CashAccountRepository;
import br.gov.sigefp.treasury.repository.TreasuryEntryRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
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.UUID;

/**
 * Serviço para nivelamento automático (sweeping) de contas de trânsito.
 * Conforme Master Plan - Module 3: Unified Account Structure (CUT).
 * 
 * Regra de Ouro UEMOA: Se o saldo na Conta de Trânsito > 0 no fim do dia,
 * o sistema deve transferir automaticamente para a CUT no BCEAO.
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class SweepingService {

        private final CashAccountRepository cashAccountRepository;
        private final TreasuryEntryRepository treasuryEntryRepository;
        private final CashAccountService cashAccountService;

        /**
         * Job agendado para executar nivelamento diário às 17:00.
         * Conforme Master Plan: @Scheduled(cron = "0 0 17 * * ?")
         */
        @Scheduled(cron = "0 0 17 * * ?") // 5 PM Daily
        public void performDailySweeping() {
                log.info("Iniciando nivelamento diário de contas de trânsito...");

                // Encontrar todas as contas de trânsito com saldo > 0
                List<CashAccount> transitAccounts = cashAccountRepository.findAll().stream()
                                .filter(account -> "TRANSIT".equals(account.getCategory()))
                                .filter(account -> account.getCurrentBalance().compareTo(BigDecimal.ZERO) > 0)
                                .filter(CashAccount::getIsActive)
                                .toList();

                if (transitAccounts.isEmpty()) {
                        log.info("Nenhuma conta de trânsito com saldo encontrada para nivelamento");
                        return;
                }

                int sweptCount = 0;
                BigDecimal totalSwept = BigDecimal.ZERO;

                for (CashAccount transitAccount : transitAccounts) {
                        try {
                                BigDecimal balance = transitAccount.getCurrentBalance();

                                // Verificar se tem conta pai (CUT)
                                if (transitAccount.getParentId() == null) {
                                        log.warn("Conta de trânsito sem conta pai (CUT): accountId={}, code={}",
                                                        transitAccount.getId(), transitAccount.getCode());
                                        continue;
                                }

                                CashAccount parentAccount = cashAccountRepository.findById(transitAccount.getParentId())
                                                .orElse(null);

                                if (parentAccount == null) {
                                        log.warn("Conta pai (CUT) não encontrada: parentId={}, transitAccount={}",
                                                        transitAccount.getParentId(), transitAccount.getCode());
                                        continue;
                                }

                                // Realizar transferência (sweeping)
                                sweepAccount(transitAccount, parentAccount, balance);

                                sweptCount++;
                                totalSwept = totalSwept.add(balance);

                                log.info("Conta nivelada: transitAccount={}, balance={}, parentAccount={}",
                                                transitAccount.getCode(), balance, parentAccount.getCode());

                        } catch (Exception e) {
                                log.error("Erro ao nivelar conta de trânsito: accountId={}, code={}, error={}",
                                                transitAccount.getId(), transitAccount.getCode(), e.getMessage(), e);
                        }
                }

                log.info("Nivelamento diário concluído: {} contas niveladas, total transferido: {}",
                                sweptCount, totalSwept);
        }

        /**
         * Realiza o nivelamento de uma conta de trânsito para a conta pai (CUT).
         */
        public void sweepAccount(CashAccount transitAccount, CashAccount parentAccount, BigDecimal amount) {
                if (amount.compareTo(BigDecimal.ZERO) <= 0) {
                        throw new IllegalArgumentException("Valor do nivelamento deve ser positivo");
                }

                if (transitAccount.getCurrentBalance().subtract(amount).compareTo(BigDecimal.ZERO) < 0) {
                        throw new IllegalStateException(
                                        String.format("Saldo insuficiente na conta de trânsito. Disponível: %s, Solicitado: %s",
                                                        transitAccount.getCurrentBalance(), amount));
                }

                LocalDate today = LocalDate.now();

                // Criar entrada de tesouraria para a conta de trânsito (saída)
                TreasuryEntry transitEntry = TreasuryEntry.builder()
                                .cashAccount(transitAccount)
                                .type(TreasuryEntryType.CASH_WITHDRAWAL)
                                .amount(amount)
                                .transactionDate(today)
                                .documentReference("Nivelamento Automático - " + today)
                                .description(String.format("Nivelamento automático para CUT: %s",
                                                parentAccount.getCode()))
                                .status("EXECUTED")
                                .build();

                // Criar entrada de tesouraria para a conta pai (entrada)
                TreasuryEntry parentEntry = TreasuryEntry.builder()
                                .cashAccount(parentAccount)
                                .type(TreasuryEntryType.CASH_DEPOSIT)
                                .amount(amount)
                                .transactionDate(today)
                                .documentReference("Nivelamento Automático - " + today)
                                .description(String.format("Nivelamento recebido de: %s", transitAccount.getCode()))
                                .status("EXECUTED")
                                .build();

                treasuryEntryRepository.save(transitEntry);
                treasuryEntryRepository.save(parentEntry);

                // Atualizar saldos
                cashAccountService.updateBalance(transitAccount.getId(), amount, "OUTFLOW");
                cashAccountService.updateBalance(parentAccount.getId(), amount, "INFLOW");

                log.info("Nivelamento executado: {} transferido de {} para {}",
                                amount, transitAccount.getCode(), parentAccount.getCode());
        }

        /**
         * Executa nivelamento manual de uma conta específica.
         */
        public void sweepAccountManually(UUID transitAccountId) {
                CashAccount transitAccount = cashAccountRepository.findById(transitAccountId)
                                .orElseThrow(() -> new br.gov.sigefp.common.exception.ResourceNotFoundException(
                                                "Conta de trânsito não encontrada: " + transitAccountId));

                if (!"TRANSIT".equals(transitAccount.getCategory())) {
                        throw new br.gov.sigefp.common.exception.BusinessException(
                                        "A conta especificada não é uma conta de trânsito",
                                        "INVALID_ACCOUNT_TYPE",
                                        org.springframework.http.HttpStatus.BAD_REQUEST);
                }

                BigDecimal balance = transitAccount.getCurrentBalance();

                if (balance.compareTo(BigDecimal.ZERO) <= 0) {
                        log.info("Conta de trânsito sem saldo para nivelar: accountId={}", transitAccountId);
                        return;
                }

                if (transitAccount.getParentId() == null) {
                        throw new br.gov.sigefp.common.exception.BusinessException(
                                        "Conta de trânsito sem conta pai (CUT) configurada",
                                        "MISSING_PARENT_ACCOUNT",
                                        org.springframework.http.HttpStatus.BAD_REQUEST);
                }

                CashAccount parentAccount = cashAccountRepository.findById(transitAccount.getParentId())
                                .orElseThrow(() -> new br.gov.sigefp.common.exception.ResourceNotFoundException(
                                                "Conta pai (CUT) não encontrada: " + transitAccount.getParentId()));

                sweepAccount(transitAccount, parentAccount, balance);
        }
}
