package dev.lions.unionflow.server.payment.pispi;

import dev.lions.unionflow.server.api.payment.CheckoutRequest;
import dev.lions.unionflow.server.api.payment.PaymentEvent;
import dev.lions.unionflow.server.api.payment.PaymentStatus;
import dev.lions.unionflow.server.payment.pispi.dto.Pacs002Response;
import dev.lions.unionflow.server.payment.pispi.dto.Pacs008Request;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

class PispiIso20022MapperTest {

    private PispiIso20022Mapper mapper;

    @BeforeEach
    void setUp() {
        mapper = new PispiIso20022Mapper();
    }

    private CheckoutRequest buildRequest(String reference) {
        return new CheckoutRequest(
                new BigDecimal("5000"),
                "XOF",
                "+2250700000001",
                "user@test.ci",
                reference,
                "https://ok.ci",
                "https://cancel.ci",
                Map.of("customerName", "Kofi Mensah")
        );
    }

    @Test
    @DisplayName("toPacs008 : endToEndId égal à la référence courte")
    void toPacs008_setsEndToEndIdFromReference() {
        CheckoutRequest req = buildRequest("REF-2026-001");
        Pacs008Request pacs = mapper.toPacs008(req, "BCEAOCIAB");
        assertThat(pacs.getEndToEndId()).isEqualTo("REF-2026-001");
    }

    @Test
    @DisplayName("toPacs008 : référence de 40 chars tronquée à 35")
    void toPacs008_truncatesLongReference() {
        String longRef = "A".repeat(40);
        CheckoutRequest req = buildRequest(longRef);
        Pacs008Request pacs = mapper.toPacs008(req, "BCEAOCIAB");
        assertThat(pacs.getEndToEndId()).hasSize(35);
        assertThat(pacs.getEndToEndId()).isEqualTo("A".repeat(35));
    }

    @Test
    @DisplayName("fromPacs002Status ACSC → SUCCESS")
    void fromPacs002Status_ACSC_returnsSuccess() {
        assertThat(mapper.fromPacs002Status("ACSC")).isEqualTo(PaymentStatus.SUCCESS);
    }

    @Test
    @DisplayName("fromPacs002Status ACSP → PROCESSING")
    void fromPacs002Status_ACSP_returnsProcessing() {
        assertThat(mapper.fromPacs002Status("ACSP")).isEqualTo(PaymentStatus.PROCESSING);
    }

    @Test
    @DisplayName("fromPacs002Status RJCT → FAILED")
    void fromPacs002Status_RJCT_returnsFailed() {
        assertThat(mapper.fromPacs002Status("RJCT")).isEqualTo(PaymentStatus.FAILED);
    }

    @Test
    @DisplayName("fromPacs002Status code inconnu → PROCESSING")
    void fromPacs002Status_unknown_returnsProcessing() {
        assertThat(mapper.fromPacs002Status("XXXX")).isEqualTo(PaymentStatus.PROCESSING);
    }

    @Test
    @DisplayName("fromPacs002 construit le PaymentEvent correctement")
    void fromPacs002_buildsEventCorrectly() {
        Pacs002Response resp = new Pacs002Response();
        resp.setClearingSystemReference("BCEAO-99999");
        resp.setOriginalEndToEndId("REF-SOUSCRIPTION-007");
        resp.setTransactionStatus("ACSC");
        Instant ts = Instant.parse("2026-04-20T12:00:00Z");
        resp.setAcceptanceDateTime(ts);

        PaymentEvent event = mapper.fromPacs002(resp);

        assertThat(event.externalId()).isEqualTo("BCEAO-99999");
        assertThat(event.reference()).isEqualTo("REF-SOUSCRIPTION-007");
        assertThat(event.status()).isEqualTo(PaymentStatus.SUCCESS);
        assertThat(event.amountConfirmed()).isNull();
        assertThat(event.transactionCode()).isEqualTo("BCEAO-99999");
        assertThat(event.occurredAt()).isEqualTo(ts);
    }

    @Test
    @DisplayName("fromPacs002 utilise Instant.now() quand acceptanceDateTime est null")
    void fromPacs002_usesNowWhenAcceptanceDateTimeNull() {
        Pacs002Response resp = new Pacs002Response();
        resp.setClearingSystemReference("REF");
        resp.setOriginalEndToEndId("E2E");
        resp.setTransactionStatus("PDNG");
        resp.setAcceptanceDateTime(null);

        Instant before = Instant.now();
        PaymentEvent event = mapper.fromPacs002(resp);
        Instant after = Instant.now();

        assertThat(event.occurredAt()).isBetween(before, after);
    }
}
