package br.gov.sigefp.treasury.service;

import br.gov.sigefp.treasury.api.dto.*;
import br.gov.sigefp.treasury.domain.CashAccount;
import br.gov.sigefp.treasury.domain.TreasuryEntryType;
import br.gov.sigefp.treasury.repository.CashAccountRepository;
import br.gov.sigefp.treasury.repository.PaymentBatchRepository;
import br.gov.sigefp.treasury.repository.PaymentOrderRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

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

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Transactional
class TreasuryIntegrationTest {

    @Autowired
    private CashAccountService cashAccountService;

    @Autowired
    private PaymentOrderService paymentOrderService;

    @Autowired
    private PaymentAuthorizationService authorizationService;

    @Autowired
    private TreasuryEntryService treasuryEntryService;

    @Autowired
    private CashAccountRepository cashAccountRepository;

    @Autowired
    private PaymentBatchRepository paymentBatchRepository;

    @Autowired
    private PaymentOrderRepository paymentOrderRepository;

    private UUID cashAccountId;
    private UUID paymentBatchId;
    private UUID paymentOrderId;
    private UUID requesterId = UUID.randomUUID();
    private UUID approverId = UUID.randomUUID();

    @BeforeEach
    void setUp() {
        // 1. Create Cash Account with funds
        CreateCashAccountDTO accountDTO = new CreateCashAccountDTO();
        accountDTO.setCode("TEST-TSA-" + UUID.randomUUID());
        accountDTO.setName("Conta Única de Teste");
        accountDTO.setType("BANK_ACCOUNT");
        accountDTO.setBankId(UUID.randomUUID());
        CashAccountDTO account = cashAccountService.create(accountDTO);
        cashAccountId = account.getId();

        // Deposit funds (1,000,000 XOF)
        treasuryEntryService.create(CreateTreasuryEntryDTO.builder()
                .cashAccountId(cashAccountId)
                .type(TreasuryEntryType.CASH_DEPOSIT)
                .amount(new BigDecimal("1000000"))
                .transactionDate(LocalDate.now())
                .description("Dotação Inicial de Caixa")
                .status("EXECUTED")
                .build());

        // 2. Create Payment Batch (Mocking simple Batch creation as Service not fully
        // mocked here)
        // Ideally use PaymentBatchService, but for speed injecting repo is fine
        // regarding this integration test scope
        // Actually lets use a "virtual" batch ID for order creation if allowed or
        // create raw entity
        // To avoid dependency hell, I'll simulate order creation directly if possible
        // or create a mock batch
        // Using PaymentOrderService.create requires a real batch in repo usually

        br.gov.sigefp.treasury.domain.PaymentBatch batch = br.gov.sigefp.treasury.domain.PaymentBatch.builder()
                .status("OPEN")
                .build();
        paymentBatchRepository.save(batch);
        paymentBatchId = batch.getId();

        // 3. Create Payment Order (150,000 XOF - Requires Level 2 Approval)
        CreatePaymentOrderDTO orderDTO = CreatePaymentOrderDTO.builder()
                .paymentBatchId(paymentBatchId)
                .agentId(UUID.randomUUID())
                .bankAccountId(UUID.randomUUID()) // Dummy bank account
                .grossAmount(new BigDecimal("150000"))
                .netAmount(new BigDecimal("150000"))
                .build();

        PaymentOrderDTO order = paymentOrderService.create(orderDTO);
        paymentOrderId = order.getId();
    }

    @Test
    @DisplayName("End-to-End: Request -> Approve -> Execute Payment")
    void testEndToEndPaymentFlow() {
        // Step 1: Request Authorization
        // 150k requires Level 1 (<=100k) or Level 2?
        // Logic: <= 100k -> 1; <= 500k -> 2. So 150k is Level 2.
        Integer requiredLevel = authorizationService.calculateRequiredLevel(new BigDecimal("150000"));
        assertEquals(2, requiredLevel, "Should require 2 levels of approval");

        CreatePaymentAuthorizationDTO authReq = CreatePaymentAuthorizationDTO.builder()
                .paymentOrderId(paymentOrderId)
                .paymentBatchId(paymentBatchId)
                .requestedBy(requesterId)
                .requiredApprovalLevel(requiredLevel)
                .build();

        PaymentAuthorizationDTO auth = authorizationService.requestAuthorization(authReq);
        assertEquals("PENDING", auth.getStatus());
        assertEquals(1, auth.getCurrentApprovalLevel());

        // Step 2: Approve Level 1
        auth = authorizationService.approve(auth.getId(), approverId, "Approving Level 1");
        assertEquals("PARTIALLY_APPROVED", auth.getStatus());
        assertEquals(2, auth.getCurrentApprovalLevel());

        // Step 3: Approve Level 2
        auth = authorizationService.approve(auth.getId(), UUID.randomUUID(), "Approving Level 2 (Final)");
        assertEquals("APPROVED", auth.getStatus());

        // Step 4: Execute Payment (Treasury Entry Outflow)
        CreateTreasuryEntryDTO paymentEntry = CreateTreasuryEntryDTO.builder()
                .cashAccountId(cashAccountId)
                .paymentOrderId(paymentOrderId)
                .type(TreasuryEntryType.PAYMENT_EXECUTION)
                .amount(new BigDecimal("150000"))
                .transactionDate(LocalDate.now())
                .description("Pagamento Salário Teste")
                .status("EXECUTED")
                .build();

        TreasuryEntryDTO executedEntry = treasuryEntryService.create(paymentEntry);
        assertEquals("EXECUTED", executedEntry.getStatus());

        // Step 5: Verify Balance Impact
        // Initial: 1,000,000 -> Paid: 150,000 -> Remaining: 850,000
        BigDecimal finalBalance = cashAccountService.getCurrentBalance(cashAccountId);
        assertEquals(0, finalBalance.compareTo(new BigDecimal("850000")), "Balance should be 850,000");
    }
}
