package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.payment.*;
import dev.lions.unionflow.server.entity.SouscriptionOrganisation;
import dev.lions.unionflow.server.payment.orchestration.PaymentOrchestrator;
import dev.lions.unionflow.server.payment.orchestration.PaymentProviderRegistry;
import dev.lions.unionflow.server.repository.SouscriptionOrganisationRepository;
import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Endpoints de paiement unifiés — abstraction multi-provider.
 * Remplace à terme les endpoints Wave-spécifiques.
 */
@Slf4j
@Path("/api/paiements")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PaiementUnifieResource {

    @Inject
    PaymentOrchestrator orchestrator;

    @Inject
    PaymentProviderRegistry registry;

    @Inject
    SouscriptionOrganisationRepository souscriptionRepository;

    /**
     * Initie un paiement via le provider demandé (ou le provider par défaut).
     *
     * <p>Exemple : {@code POST /api/paiements/initier?provider=WAVE}
     */
    @POST
    @Path("/initier")
    @RolesAllowed({"MEMBRE_ACTIF", "ADMIN_ORGANISATION", "TRESORIER", "SUPER_ADMIN"})
    public Response initier(
            @QueryParam("provider") String provider,
            PaiementInitierRequest req) {
        try {
            // Si une souscription est fournie, utiliser le providerDefaut de sa formule
            String resolvedProvider = provider;
            if (req.souscriptionId() != null) {
                resolvedProvider = souscriptionRepository.findByIdOptional(req.souscriptionId())
                        .map(SouscriptionOrganisation::getFormule)
                        .map(f -> f.getProviderDefaut())
                        .filter(p -> p != null && !p.isBlank())
                        .orElse(provider);
            }

            CheckoutRequest checkoutRequest = new CheckoutRequest(
                    req.montant(),
                    req.devise() != null ? req.devise() : "XOF",
                    req.telephone(),
                    req.email(),
                    req.reference(),
                    req.successUrl(),
                    req.cancelUrl(),
                    Map.of()
            );
            CheckoutSession session = orchestrator.initierPaiement(checkoutRequest, resolvedProvider);
            return Response.ok(session).build();
        } catch (PaymentException e) {
            return Response.status(e.getHttpStatus())
                    .entity(Map.of("error", e.getMessage(), "provider", e.getProviderCode()))
                    .build();
        }
    }

    /**
     * Webhook entrant d'un provider. Vérifie la signature et met à jour le statut.
     * Route : {@code POST /api/paiements/webhook/{provider}}
     */
    @POST
    @Path("/webhook/{provider}")
    @PermitAll
    @Consumes(MediaType.WILDCARD)
    public Response webhook(
            @PathParam("provider") String providerCode,
            String rawBody,
            @Context HttpHeaders httpHeaders) {
        try {
            PaymentProvider provider = registry.get(providerCode.toUpperCase());
            Map<String, String> headers = httpHeaders.getRequestHeaders().entrySet().stream()
                    .collect(Collectors.toMap(
                            Map.Entry::getKey,
                            e -> e.getValue().isEmpty() ? "" : e.getValue().get(0)
                    ));

            PaymentEvent event = provider.processWebhook(rawBody, headers);
            orchestrator.handleEvent(event);
            return Response.ok().build();

        } catch (UnsupportedOperationException e) {
            return Response.status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", "Provider inconnu : " + providerCode))
                    .build();
        } catch (PaymentException e) {
            log.error("Webhook {} rejeté : {}", providerCode, e.getMessage());
            return Response.status(e.getHttpStatus())
                    .entity(Map.of("error", e.getMessage()))
                    .build();
        }
    }

    /** Retourne les providers de paiement disponibles. */
    @GET
    @Path("/providers")
    @RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
    public List<String> getProviders() {
        return registry.getAvailableCodes();
    }

    public record PaiementInitierRequest(
            BigDecimal montant,
            String devise,
            String telephone,
            String email,
            String reference,
            String successUrl,
            String cancelUrl,
            /** Optionnel — si fourni, le providerDefaut de la formule prend le dessus sur le query param. */
            UUID souscriptionId
    ) {}
}
