package br.gov.sigefp.budget.integration;

import br.gov.sigefp.budget.api.dto.BudgetExecutionDTO;
import br.gov.sigefp.budget.service.BudgetExecutionService;
import br.gov.sigefp.common.exception.BusinessException;
import br.gov.sigefp.common.exception.InsufficientBudgetException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.UUID;

/**
 * Serviço de integração para criar execuções orçamentárias a partir de outros
 * módulos.
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class BudgetIntegrationService {

    private final BudgetExecutionService budgetExecutionService;

    /**
     * Cria execução orçamentária do tipo COMMITMENT a partir de um item de folha.
     */
    @Transactional
    public void createCommitmentFromPayrollItem(UUID budgetLineId, Long periodId, BigDecimal amount, UUID referenceId) {
        if (budgetLineId == null || periodId == null || amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        try {
            BudgetExecutionDTO execution = BudgetExecutionDTO.builder()
                    .budgetLineId(budgetLineId)
                    .periodId(periodId)
                    .movementType("COMMITMENT")
                    .amount(amount)
                    .sourceModule("rh")
                    .referenceId(referenceId)
                    .build();

            budgetExecutionService.registerExecution(execution);
        } catch (InsufficientBudgetException e) {
            log.error("Saldo insuficiente ao criar execução orçamentária (COMMITMENT)", e);
            throw e; // Re-throw exceção específica
        } catch (BusinessException e) {
            log.error("Erro de negócio ao criar execução orçamentária (COMMITMENT)", e);
            throw e; // Re-throw exceção específica
        } catch (Exception e) {
            log.error("Erro inesperado ao criar execução orçamentária (COMMITMENT)", e);
            throw new BusinessException("Erro ao criar execução orçamentária (COMMITMENT)",
                    "EXECUTION_ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Cria execução orçamentária do tipo PAYMENT a partir de um pagamento da
     * tesouraria.
     */
    @Transactional
    public void createPaymentFromTreasury(UUID budgetLineId, Long periodId, BigDecimal amount, UUID referenceId) {
        if (budgetLineId == null || periodId == null || amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        try {
            BudgetExecutionDTO execution = BudgetExecutionDTO.builder()
                    .budgetLineId(budgetLineId)
                    .periodId(periodId)
                    .movementType("PAYMENT")
                    .amount(amount)
                    .sourceModule("treasury")
                    .referenceId(referenceId)
                    .build();

            budgetExecutionService.registerExecution(execution);
        } catch (InsufficientBudgetException e) {
            log.error("Saldo insuficiente ao criar execução orçamentária (PAYMENT)", e);
            throw e;
        } catch (BusinessException e) {
            log.error("Erro de negócio ao criar execução orçamentária (PAYMENT)", e);
            throw e;
        } catch (Exception e) {
            log.error("Erro inesperado ao criar execução orçamentária (PAYMENT)", e);
            throw new BusinessException("Erro ao criar execução orçamentária (PAYMENT)",
                    "EXECUTION_ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Cria execução orçamentária do tipo LIQUIDATION a partir de um item de folha
     * (no encerramento).
     */
    @Transactional
    public void createLiquidationFromPayrollItem(UUID budgetLineId, Long periodId, BigDecimal amount,
            UUID referenceId) {
        if (budgetLineId == null || periodId == null || amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        try {
            BudgetExecutionDTO execution = BudgetExecutionDTO.builder()
                    .budgetLineId(budgetLineId)
                    .periodId(periodId)
                    .movementType("LIQUIDATION")
                    .amount(amount)
                    .sourceModule("rh")
                    .referenceId(referenceId)
                    .build();

            budgetExecutionService.registerExecution(execution);
        } catch (InsufficientBudgetException e) {
            log.error("Saldo insuficiente ao criar execução orçamentária (LIQUIDATION)", e);
            throw e;
        } catch (BusinessException e) {
            log.error("Erro de negócio ao criar execução orçamentária (LIQUIDATION)", e);
            throw e;
        } catch (Exception e) {
            log.error("Erro inesperado ao criar execução orçamentária (LIQUIDATION)", e);
            throw new BusinessException("Erro ao criar execução orçamentária (LIQUIDATION)",
                    "EXECUTION_ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Converte um PayrollPeriod (fiscalYear + month) para um periodId Long (formato
     * YYYYMM).
     */
    public static Long toPeriodId(Integer fiscalYear, Integer month) {
        if (fiscalYear == null || month == null) {
            return null;
        }
        return Long.valueOf(fiscalYear * 100 + month);
    }
}
