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.ApprovalDTO;
import br.gov.sigefp.treasury.api.dto.PaymentAuthorizationDTO;
import br.gov.sigefp.treasury.api.dto.CreatePaymentAuthorizationDTO;
import br.gov.sigefp.treasury.domain.Approval;
import br.gov.sigefp.treasury.domain.PaymentAuthorization;
import br.gov.sigefp.treasury.repository.ApprovalRepository;
import br.gov.sigefp.treasury.repository.PaymentAuthorizationRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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 autorizações de pagamento.
 * Gerencia workflow hierárquico de aprovação.
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class PaymentAuthorizationService {

    private final PaymentAuthorizationRepository authorizationRepository;
    private final ApprovalRepository approvalRepository;
    private final TreasuryPlanService treasuryPlanService;
    private final br.gov.sigefp.treasury.service.PaymentOrderService paymentOrderService;

    public PaymentAuthorizationDTO requestAuthorization(CreatePaymentAuthorizationDTO dto) {
        PaymentAuthorization authorization = PaymentAuthorization.builder()
                .paymentOrderId(dto.getPaymentOrderId())
                .paymentBatchId(dto.getPaymentBatchId())
                .requestedBy(dto.getRequestedBy())
                .requestedAt(LocalDateTime.now())
                .requiredApprovalLevel(dto.getRequiredApprovalLevel())
                .currentApprovalLevel(1)
                .status("PENDING")
                .build();

        PaymentAuthorization saved = authorizationRepository.save(authorization);
        log.info("Autorização solicitada: id={}, requiredLevel={}, paymentOrderId={}",
                saved.getId(), saved.getRequiredApprovalLevel(), saved.getPaymentOrderId());

        return toDTO(saved);
    }

    public PaymentAuthorizationDTO approve(UUID authorizationId, UUID approverId, String comments) {
        PaymentAuthorization authorization = authorizationRepository.findById(authorizationId)
                .orElseThrow(() -> new ResourceNotFoundException(
                        "Autorização não encontrada: " + authorizationId));

        if (!"PENDING".equals(authorization.getStatus()) &&
                !"PARTIALLY_APPROVED".equals(authorization.getStatus())) {
            throw new BusinessException(
                    "Apenas autorizações pendentes ou parcialmente aprovadas podem ser aprovadas",
                    "INVALID_STATUS",
                    org.springframework.http.HttpStatus.CONFLICT);
        }

        // Registrar aprovação
        Approval approval = Approval.builder()
                .authorization(authorization)
                .level(authorization.getCurrentApprovalLevel())
                .approvedBy(approverId)
                .approvedAt(LocalDateTime.now())
                .comments(comments)
                .build();

        approvalRepository.save(approval);
        authorization.getApprovals().add(approval);

        // Verificar se precisa de mais aprovações
        if (authorization.getCurrentApprovalLevel() >= authorization.getRequiredApprovalLevel()) {

            // INTEGRAÇÃO TESOURO (Fase 3.1): Validar Teto Financeiro antes de Aprovar
            br.gov.sigefp.treasury.api.dto.PaymentOrderDTO paymentOrder = paymentOrderService
                    .findById(authorization.getPaymentOrderId());

            try {
                // 1. Validar disponibilidade
                treasuryPlanService.validateAvailability(paymentOrder.getGrossAmount());

                // 2. Consumir cota (Atualizar valor executado)
                br.gov.sigefp.treasury.api.dto.TreasuryPlanDTO activePlan = treasuryPlanService
                        .findActivePlanForDate(java.time.LocalDate.now());
                if (activePlan != null) {
                    treasuryPlanService.updateExecutedAmount(activePlan.getId(), paymentOrder.getGrossAmount());
                }

                authorization.setStatus("APPROVED");
                log.info("Autorização totalmente aprovada e teto consumido: id={}, planId={}, amount={}",
                        authorizationId, activePlan != null ? activePlan.getId() : "N/A",
                        paymentOrder.getGrossAmount());

            } catch (BusinessException e) {
                if ("CEILING_EXCEEDED".equals(e.getCode())) {
                    log.warn("Autorização rejeitada por falta de teto: id={}, amount={}", authorizationId,
                            paymentOrder.getGrossAmount());
                    throw e; // Repassa a exceção para o controller
                }
                throw e;
            }

        } else {
            authorization.setCurrentApprovalLevel(authorization.getCurrentApprovalLevel() + 1);
            authorization.setStatus("PARTIALLY_APPROVED");
            log.info("Autorização parcialmente aprovada: id={}, currentLevel={}",
                    authorizationId, authorization.getCurrentApprovalLevel());
        }

        PaymentAuthorization saved = authorizationRepository.save(authorization);
        return toDTO(saved);
    }

    public PaymentAuthorizationDTO reject(UUID authorizationId, UUID approverId, String reason) {
        PaymentAuthorization authorization = authorizationRepository.findById(authorizationId)
                .orElseThrow(() -> new ResourceNotFoundException(
                        "Autorização não encontrada: " + authorizationId));

        if ("APPROVED".equals(authorization.getStatus()) ||
                "REJECTED".equals(authorization.getStatus())) {
            throw new BusinessException(
                    "Autorização já foi finalizada",
                    "ALREADY_FINALIZED",
                    org.springframework.http.HttpStatus.CONFLICT);
        }

        authorization.setStatus("REJECTED");
        authorization.setRejectionReason(reason);

        PaymentAuthorization saved = authorizationRepository.save(authorization);
        log.info("Autorização rejeitada: id={}, reason={}", authorizationId, reason);

        return toDTO(saved);
    }

    @Transactional(readOnly = true)
    public PaymentAuthorizationDTO findById(UUID id) {
        PaymentAuthorization authorization = authorizationRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Autorização não encontrada: " + id));
        return toDTO(authorization);
    }

    @Transactional(readOnly = true)
    public List<PaymentAuthorizationDTO> findPendingApprovals(UUID approverId) {
        // Retorna todas as autorizações pendentes que o aprovador pode aprovar
        // (assumindo que o aprovador pode aprovar até o nível máximo)
        return authorizationRepository.findPendingApprovals(Integer.MAX_VALUE).stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public List<PaymentAuthorizationDTO> findByPaymentOrderId(UUID paymentOrderId) {
        return authorizationRepository.findByPaymentOrderId(paymentOrderId).stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public List<PaymentAuthorizationDTO> findByPaymentBatchId(UUID paymentBatchId) {
        return authorizationRepository.findByPaymentBatchId(paymentBatchId).stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    /**
     * Calcula o nível de aprovação necessário baseado no valor do pagamento.
     */
    public Integer calculateRequiredLevel(java.math.BigDecimal amount) {
        // Valores até 100.000 XOF: 1 nível
        // Valores 100.001 - 500.000 XOF: 2 níveis
        // Valores acima de 500.000 XOF: 3 níveis
        if (amount.compareTo(new java.math.BigDecimal("100000")) <= 0) {
            return 1;
        } else if (amount.compareTo(new java.math.BigDecimal("500000")) <= 0) {
            return 2;
        } else {
            return 3;
        }
    }

    private PaymentAuthorizationDTO toDTO(PaymentAuthorization authorization) {
        List<ApprovalDTO> approvals = authorization.getApprovals().stream()
                .map(this::approvalToDTO)
                .collect(Collectors.toList());

        return PaymentAuthorizationDTO.builder()
                .id(authorization.getId())
                .paymentOrderId(authorization.getPaymentOrderId())
                .paymentBatchId(authorization.getPaymentBatchId())
                .requestedBy(authorization.getRequestedBy())
                .requestedAt(authorization.getRequestedAt())
                .requiredApprovalLevel(authorization.getRequiredApprovalLevel())
                .currentApprovalLevel(authorization.getCurrentApprovalLevel())
                .status(authorization.getStatus())
                .rejectionReason(authorization.getRejectionReason())
                .approvals(approvals)
                .createdAt(authorization.getCreatedAt())
                .updatedAt(authorization.getUpdatedAt())
                .build();
    }

    private ApprovalDTO approvalToDTO(Approval approval) {
        return ApprovalDTO.builder()
                .id(approval.getId())
                .authorizationId(approval.getAuthorization().getId())
                .level(approval.getLevel())
                .approvedBy(approval.getApprovedBy())
                .approvedAt(approval.getApprovedAt())
                .comments(approval.getComments())
                .signatureHash(approval.getSignatureHash())
                .build();
    }
}
