package br.gov.sigefp.treasury.service;

import br.gov.sigefp.rh.domain.AgentBankAccount;
import br.gov.sigefp.rh.domain.PayrollItem;
import br.gov.sigefp.rh.domain.PayrollPeriod;
import br.gov.sigefp.rh.domain.PayrollRun;
import br.gov.sigefp.rh.repository.AgentBankAccountRepository;
import br.gov.sigefp.rh.repository.PayrollItemRepository;
import br.gov.sigefp.rh.repository.PayrollRunRepository;
import br.gov.sigefp.treasury.api.dto.PaymentOrderDTO;
import br.gov.sigefp.treasury.domain.PaymentBatch;
import br.gov.sigefp.treasury.domain.PaymentOrder;
import br.gov.sigefp.treasury.repository.PaymentBatchRepository;
import br.gov.sigefp.treasury.repository.PaymentOrderRepository;
import br.gov.sigefp.common.exception.BusinessException;
import br.gov.sigefp.common.exception.ResourceNotFoundException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

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

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class PaymentOrderServiceTest {

    @Mock
    private PaymentOrderRepository paymentOrderRepository;
    @Mock
    private PaymentBatchRepository paymentBatchRepository;
    @Mock
    private PayrollRunRepository payrollRunRepository;
    @Mock
    private PayrollItemRepository payrollItemRepository;
    @Mock
    private AgentBankAccountRepository agentBankAccountRepository;

    @InjectMocks
    private PaymentOrderService paymentOrderService;

    private UUID payrollRunId;
    private UUID batchId;
    private UUID agentId;
    private PayrollRun payrollRun;
    private PaymentBatch batch;

    @BeforeEach
    void setUp() {
        payrollRunId = UUID.randomUUID();
        batchId = UUID.randomUUID();
        agentId = UUID.randomUUID();

        PayrollPeriod period = PayrollPeriod.builder().fiscalYear(2024).month(1).build();
        payrollRun = PayrollRun.builder().status("COMPLETED").period(period).build();
        payrollRun.setId(payrollRunId);
        batch = PaymentBatch.builder().build();
        batch.setId(batchId);
    }

    @Test
    @DisplayName("Deve gerar ordens de pagamento agrupadas por agente corretamente")
    void generateOrdersFromPayrollRun_Success() {
        // Mocks
        when(payrollRunRepository.findById(payrollRunId)).thenReturn(Optional.of(payrollRun));
        when(paymentBatchRepository.findById(batchId)).thenReturn(Optional.of(batch));

        // Itens de Folha para o mesmo Agente: 1 Earning (1000) e 1 Deduction (200)
        PayrollItem earning = PayrollItem.builder()
                .agent(agentId)
                .lineType("EARNING")
                .totalAmount(new BigDecimal("1000"))
                .build();

        PayrollItem deduction = PayrollItem.builder()
                .agent(agentId)
                .lineType("DEDUCTION")
                .totalAmount(new BigDecimal("200"))
                .build();

        when(payrollItemRepository.findByPayrollRunId(payrollRunId)).thenReturn(List.of(earning, deduction));

        AgentBankAccount account = AgentBankAccount.builder().build();
        account.setId(UUID.randomUUID());
        when(agentBankAccountRepository.findByAgentIdAndIsPrimaryTrue(agentId)).thenReturn(Optional.of(account));

        when(paymentOrderRepository.save(any(PaymentOrder.class))).thenAnswer(i -> {
            PaymentOrder po = i.getArgument(0);
            po.setId(UUID.randomUUID());
            return po;
        });

        // Execute
        List<PaymentOrderDTO> result = paymentOrderService.generateOrdersInternal(payrollRunId, batchId);

        // Verify
        assertEquals(1, result.size());
        PaymentOrderDTO order = result.get(0);
        assertEquals(new BigDecimal("1000"), order.getGrossAmount());
        assertEquals(new BigDecimal("800"), order.getNetAmount());
        assertEquals(agentId, order.getAgentId());
        assertEquals(payrollRunId, order.getPayrollRunId()); // Agora é UUID nativo!

        verify(paymentOrderRepository, times(1)).save(any(PaymentOrder.class));
    }

    @Test
    @DisplayName("Deve falhar se a folha não estiver completa")
    void generateOrders_FailsIfStatusNotCompleted() {
        payrollRun.setStatus("PENDING");
        when(payrollRunRepository.findById(payrollRunId)).thenReturn(Optional.of(payrollRun));

        BusinessException exception = assertThrows(BusinessException.class,
                () -> paymentOrderService.generateOrdersInternal(payrollRunId, batchId));

        assertTrue(exception.getMessage().contains("Apenas execuções de folha com status COMPLETED"));
    }
}
