package br.gov.sigefp.treasury.service;

import br.gov.sigefp.budget.integration.BudgetIntegrationService;
import br.gov.sigefp.rh.domain.PayrollItem;
import br.gov.sigefp.rh.domain.PayrollRun;
import br.gov.sigefp.rh.repository.PayrollItemRepository;
import br.gov.sigefp.rh.repository.PayrollRunRepository;
import br.gov.sigefp.treasury.api.dto.CreateTreasuryPaymentDTO;
import br.gov.sigefp.treasury.api.dto.TreasuryPaymentDTO;
import br.gov.sigefp.treasury.domain.PaymentOrder;
import br.gov.sigefp.treasury.domain.TreasuryPayment;
import br.gov.sigefp.treasury.repository.PaymentOrderRepository;
import br.gov.sigefp.treasury.repository.TreasuryPaymentRepository;
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.time.Instant;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Serviço de aplicação para gestão de pagamentos efetivados pela tesouraria.
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class TreasuryPaymentService {

    private final TreasuryPaymentRepository treasuryPaymentRepository;
    private final PaymentOrderRepository paymentOrderRepository;
    private final BudgetIntegrationService budgetIntegrationService;

    /**
     * Registrar confirmação de pagamento (paidAt, transactionRef, status, message).
     */
    public TreasuryPaymentDTO registerPayment(CreateTreasuryPaymentDTO dto) {
        PaymentOrder paymentOrder = paymentOrderRepository.findById(dto.getPaymentOrderId())
                .orElseThrow(() -> new IllegalArgumentException(
                        "Ordem de pagamento não encontrada: " + dto.getPaymentOrderId()));

        // 1. Perna Líquida (Vendor)
        TreasuryPayment netPayment = TreasuryPayment.builder()
                .paymentOrder(paymentOrder)
                .paidAt(dto.getPaidAt() != null ? dto.getPaidAt() : Instant.now())
                .transactionRef(dto.getTransactionRef())
                .status(dto.getStatus())
                .message(dto.getMessage() != null ? dto.getMessage() : "Pagamento Líquido ao Beneficiário")
                .build();

        TreasuryPayment savedNet = treasuryPaymentRepository.save(netPayment);
        log.info("Pagamento Líquido registrado: id={}, amount={}", savedNet.getId(), paymentOrder.getNetAmount());

        // 2. Perna Fiscal (Integridade RN03) - Apenas se houver imposto retido e status
        // for PAID
        if ("PAID".equals(dto.getStatus()) && paymentOrder.getTaxAmount() != null &&
                paymentOrder.getTaxAmount().compareTo(java.math.BigDecimal.ZERO) > 0) {

            TreasuryPayment taxPayment = TreasuryPayment.builder()
                    .paymentOrder(paymentOrder)
                    .paidAt(dto.getPaidAt() != null ? dto.getPaidAt() : Instant.now())
                    .transactionRef(dto.getTransactionRef() + "-TAX")
                    .status("PAID")
                    .message("Retenção na Fonte: " + paymentOrder.getTaxRetentionType())
                    .build();

            TreasuryPayment savedTax = treasuryPaymentRepository.save(taxPayment);
            log.info("Retenção Fiscal registrada: id={}, amount={}, targetAccount={}",
                    savedTax.getId(), paymentOrder.getTaxAmount(), paymentOrder.getTaxCollectionAccountId());
        }

        // Atualizar status da ordem de pagamento se o pagamento foi confirmado
        if ("PAID".equals(dto.getStatus())) {
            paymentOrder.setStatus("PAID");
            paymentOrderRepository.save(paymentOrder);

            // Criar execução orçamentária do tipo PAYMENT
            try {
                if (paymentOrder.getBudgetLineId() != null && paymentOrder.getGrossAmount() != null) {
                    // Calcular periodId a partir da data do pagamento
                    LocalDate paymentDate = savedNet.getPaidAt().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
                    YearMonth paymentPeriod = YearMonth.from(paymentDate);
                    Long periodId = BudgetIntegrationService.toPeriodId(
                            paymentPeriod.getYear(),
                            paymentPeriod.getMonthValue());

                    // Criar execução orçamentária do tipo PAYMENT
                    // IMPORTANTE: Execução Orçamentária deve refletir o VALOR BRUTO (Despesa Total)
                    budgetIntegrationService.createPaymentFromTreasury(
                            paymentOrder.getBudgetLineId(),
                            periodId,
                            paymentOrder.getGrossAmount(), // Alterado de netAmount para grossAmount
                            savedNet.getId());

                    log.info(
                            "Execução orçamentária (PAYMENT) criada a partir de pagamento: paymentOrderId={}, budgetLineId={}, grossAmount={}",
                            paymentOrder.getId(), paymentOrder.getBudgetLineId(), paymentOrder.getGrossAmount());
                } else {
                    log.warn(
                            "Não foi possível criar execução orçamentária: paymentOrderId={}, budgetLineId={}, amount={}",
                            paymentOrder.getId(), paymentOrder.getBudgetLineId(), paymentOrder.getGrossAmount());
                }
            } catch (Exception e) {
                log.error("Erro ao criar execução orçamentária a partir de pagamento: {}", e.getMessage(), e);
                // Não falha o registro do pagamento se a execução orçamentária falhar
            }
        } else if ("REJECTED".equals(dto.getStatus())) {
            paymentOrder.setStatus("REJECTED");
            paymentOrderRepository.save(paymentOrder);
        }

        return toDTO(savedNet);
    }

    @Transactional(readOnly = true)
    public TreasuryPaymentDTO findById(UUID id) {
        TreasuryPayment treasuryPayment = treasuryPaymentRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Pagamento não encontrado: " + id));
        return toDTO(treasuryPayment);
    }

    @Transactional(readOnly = true)
    public Page<TreasuryPaymentDTO> findAll(Pageable pageable) {
        return treasuryPaymentRepository.findAll(pageable).map(this::toDTO);
    }

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

    @Transactional(readOnly = true)
    public List<TreasuryPaymentDTO> findByStatus(String status) {
        return treasuryPaymentRepository.findByStatus(status).stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public List<TreasuryPaymentDTO> findByPaymentOrderIdAndStatus(UUID paymentOrderId, String status) {
        return treasuryPaymentRepository.findByPaymentOrderIdAndStatus(paymentOrderId, status).stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    private TreasuryPaymentDTO toDTO(TreasuryPayment treasuryPayment) {
        return TreasuryPaymentDTO.builder()
                .id(treasuryPayment.getId())
                .paymentOrderId(treasuryPayment.getPaymentOrder().getId())
                .paidAt(treasuryPayment.getPaidAt())
                .transactionRef(treasuryPayment.getTransactionRef())
                .status(treasuryPayment.getStatus())
                .message(treasuryPayment.getMessage())
                .build();
    }
}
