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

import dev.lions.unionflow.server.api.payment.CheckoutRequest;
import dev.lions.unionflow.server.api.payment.CheckoutSession;
import dev.lions.unionflow.server.api.payment.PaymentEvent;
import dev.lions.unionflow.server.api.payment.PaymentException;
import dev.lions.unionflow.server.api.payment.PaymentProvider;
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 jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import java.time.Instant;
import java.util.Map;
import java.util.UUID;

/**
 * Provider PI-SPI BCEAO — interopérabilité paiements instantanés UEMOA.
 *
 * <p>Sandbox : https://developer.pispi.bceao.int
 * Spec : Business API ISO 20022 pacs.008.001.10 / pacs.002.001.14
 * Deadline obligation réglementaire : 30 juin 2026
 *
 * <p>Mode mock automatique si {@code pispi.api.client-id} ou {@code pispi.institution.code} sont absents.
 */
@Slf4j
@ApplicationScoped
public class PispiPaymentProvider implements PaymentProvider {

    public static final String CODE = "PISPI";

    @Inject
    PispiClient pispiClient;

    @Inject
    PispiIso20022Mapper mapper;

    @ConfigProperty(name = "pispi.api.client-id")
    java.util.Optional<String> clientIdOpt;

    @ConfigProperty(name = "pispi.institution.code")
    java.util.Optional<String> institutionCodeOpt;

    // SmallRye Config 3.20+ : defaultValue = "" casse au boot ; utiliser Optional + orElse("").
    @ConfigProperty(name = "pispi.institution.bic")
    java.util.Optional<String> institutionBicOpt;

    String clientId;
    String institutionCode;
    String institutionBic;

    @jakarta.annotation.PostConstruct
    void init() {
        clientId = clientIdOpt.orElse("");
        institutionCode = institutionCodeOpt.orElse("");
        institutionBic = institutionBicOpt.orElse("");
    }

    @Override
    public String getProviderCode() {
        return CODE;
    }

    @Override
    public CheckoutSession initiateCheckout(CheckoutRequest request) throws PaymentException {
        if (!isConfigured()) {
            String mockId = "PISPI-MOCK-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
            log.warn("PI-SPI non configuré — mode mock pour ref={}", request.reference());
            return new CheckoutSession(
                    mockId,
                    "https://mock.pispi.bceao.int/pay/" + mockId,
                    Instant.now().plusSeconds(1800),
                    Map.of("mock", "true", "provider", CODE)
            );
        }
        Pacs008Request pacs008 = mapper.toPacs008(request, institutionBic);
        Pacs002Response pacs002 = pispiClient.initiatePayment(pacs008);
        String externalId = pacs002.getClearingSystemReference() != null
                ? pacs002.getClearingSystemReference()
                : pacs008.getEndToEndId();
        return new CheckoutSession(
                externalId,
                null,
                Instant.now().plusSeconds(1800),
                Map.of("provider", CODE, "iso", "pacs.008.001.10", "endToEndId", pacs008.getEndToEndId())
        );
    }

    @Override
    public PaymentStatus getStatus(String externalId) throws PaymentException {
        if (!isConfigured()) {
            log.warn("PI-SPI non configuré — getStatus mock pour id={}", externalId);
            return PaymentStatus.PROCESSING;
        }
        Pacs002Response pacs002 = pispiClient.getStatus(externalId);
        return mapper.fromPacs002Status(pacs002.getTransactionStatus());
    }

    @Override
    public PaymentEvent processWebhook(String rawBody, Map<String, String> headers) throws PaymentException {
        // Les webhooks PI-SPI passent par PispiWebhookResource qui valide l'IP et la signature en amont
        throw new PaymentException(CODE, "Utiliser /api/pispi/webhook directement", 400);
    }

    @Override
    public boolean isAvailable() {
        return isConfigured();
    }

    private boolean isConfigured() {
        return clientId != null && !clientId.isBlank()
                && institutionCode != null && !institutionCode.isBlank();
    }
}
