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

import dev.lions.unionflow.server.api.payment.*;
import dev.lions.unionflow.server.service.PaiementService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import java.util.List;

/**
 * Façade de paiement avec stratégie de fallback automatique.
 *
 * <p>Ordre de priorité :
 * <ol>
 *   <li>PI-SPI si disponible (obligation réglementaire BCEAO)</li>
 *   <li>Provider demandé par le client</li>
 *   <li>Wave (provider par défaut)</li>
 * </ol>
 */
@Slf4j
@ApplicationScoped
public class PaymentOrchestrator {

    @Inject
    PaymentProviderRegistry registry;

    @Inject
    PaiementService paiementService;

    @ConfigProperty(name = "payment.default-provider", defaultValue = "WAVE")
    String defaultProvider;

    @ConfigProperty(name = "payment.pispi-priority", defaultValue = "false")
    boolean pispiPriority;

    /**
     * Lance un checkout sur le provider demandé, avec fallback si indisponible.
     *
     * @param request     la requête de checkout
     * @param providerCode le provider demandé (null = provider par défaut)
     */
    public CheckoutSession initierPaiement(CheckoutRequest request, String providerCode) throws PaymentException {
        List<String> ordre = buildProviderOrder(providerCode);
        PaymentException dernierEchec = null;

        for (String code : ordre) {
            PaymentProvider provider = tryGetProvider(code);
            if (provider == null || !provider.isAvailable()) continue;

            try {
                CheckoutSession session = provider.initiateCheckout(request);
                log.info("Checkout initié via {} pour ref={}", code, request.reference());
                return session;
            } catch (PaymentException e) {
                log.warn("Provider {} échoué pour ref={}: {} — tentative fallback",
                        code, request.reference(), e.getMessage());
                dernierEchec = e;
            }
        }

        throw dernierEchec != null ? dernierEchec
                : new PaymentException("NONE", "Aucun provider de paiement disponible", 503);
    }

    /**
     * Traite un événement de paiement reçu via webhook.
     * Délègue la mise à jour métier (souscription, cotisation...) selon la référence.
     */
    public void handleEvent(PaymentEvent event) {
        log.info("PaymentEvent reçu : externalId={}, ref={}, statut={}",
                event.externalId(), event.reference(), event.status());
        paiementService.mettreAJourStatutDepuisWebhook(event);
    }

    private List<String> buildProviderOrder(String requested) {
        if (pispiPriority) {
            if (requested != null) return List.of("PISPI", requested, defaultProvider);
            return List.of("PISPI", defaultProvider);
        }
        if (requested != null) return List.of(requested, defaultProvider);
        return List.of(defaultProvider);
    }

    private PaymentProvider tryGetProvider(String code) {
        try {
            return registry.get(code);
        } catch (UnsupportedOperationException e) {
            return null;
        }
    }
}
