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

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.lions.unionflow.server.api.payment.*;
import dev.lions.unionflow.server.service.WaveCheckoutService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.HexFormat;
import java.util.Map;

/**
 * Implémentation Wave de PaymentProvider.
 *
 * <p>Délègue la création de session à {@link WaveCheckoutService} existant.
 * Normalise les webhooks Wave vers {@link PaymentEvent}.
 */
@Slf4j
@ApplicationScoped
public class WavePaymentProvider implements PaymentProvider {

    public static final String CODE = "WAVE";

    @Inject
    WaveCheckoutService waveCheckoutService;

    @ConfigProperty(name = "wave.webhook.secret", defaultValue = "")
    String webhookSecret;

    private final ObjectMapper mapper = new ObjectMapper();

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

    @Override
    public CheckoutSession initiateCheckout(CheckoutRequest request) throws PaymentException {
        try {
            String amount = request.amount().toBigInteger().toString();
            WaveCheckoutService.WaveCheckoutSessionResponse resp = waveCheckoutService.createSession(
                    amount,
                    request.currency(),
                    request.successUrl(),
                    request.cancelUrl(),
                    request.reference(),
                    request.customerPhone()
            );
            return new CheckoutSession(
                    resp.id,
                    resp.waveLaunchUrl,
                    Instant.now().plusSeconds(3600),
                    Map.of("provider", CODE)
            );
        } catch (Exception e) {
            throw new PaymentException(CODE, e.getMessage(), 500, e);
        }
    }

    @Override
    public PaymentStatus getStatus(String externalId) throws PaymentException {
        // Wave ne fournit pas d'API de polling — le statut passe par les webhooks.
        // Un polling naïf via la session URL n'est pas supporté.
        log.warn("Wave ne supporte pas le polling de statut — utiliser les webhooks.");
        return PaymentStatus.PROCESSING;
    }

    @Override
    public PaymentEvent processWebhook(String rawBody, Map<String, String> headers) throws PaymentException {
        verifierSignatureWave(rawBody, headers);

        try {
            JsonNode root = mapper.readTree(rawBody);
            String type = root.path("type").asText();
            JsonNode data = root.path("data");

            String externalId = data.path("id").asText(null);
            String clientRef = data.path("client_reference").asText(null);
            String rawAmount = data.path("amount").asText("0");
            BigDecimal amount = new BigDecimal(rawAmount);

            PaymentStatus status = switch (type) {
                case "checkout.session.completed" -> PaymentStatus.SUCCESS;
                case "checkout.session.failed"    -> PaymentStatus.FAILED;
                case "checkout.session.expired"   -> PaymentStatus.EXPIRED;
                default -> PaymentStatus.PROCESSING;
            };

            return new PaymentEvent(
                    externalId,
                    clientRef,
                    status,
                    amount,
                    data.path("transaction_id").asText(null),
                    Instant.now()
            );
        } catch (Exception e) {
            throw new PaymentException(CODE, "Webhook Wave malformé : " + e.getMessage(), 400, e);
        }
    }

    private void verifierSignatureWave(String rawBody, Map<String, String> headers) throws PaymentException {
        if (webhookSecret == null || webhookSecret.isBlank()) return;

        String sigHeader = headers.get("wave-signature");
        if (sigHeader == null) sigHeader = headers.get("Wave-Signature");
        if (sigHeader == null) {
            throw new PaymentException(CODE, "Signature webhook Wave absente", 401);
        }

        try {
            String timestamp = "";
            String receivedSig = "";
            for (String part : sigHeader.split(",")) {
                if (part.startsWith("t=")) timestamp = part.substring(2);
                if (part.startsWith("v1=")) receivedSig = part.substring(3);
            }

            String payload = timestamp + "." + rawBody;
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(webhookSecret.getBytes(), "HmacSHA256"));
            String computed = HexFormat.of().formatHex(mac.doFinal(payload.getBytes()));

            if (!java.security.MessageDigest.isEqual(computed.getBytes(), receivedSig.getBytes())) {
                throw new PaymentException(CODE, "Signature webhook Wave invalide", 401);
            }
        } catch (PaymentException e) {
            throw e;
        } catch (Exception e) {
            throw new PaymentException(CODE, "Erreur vérification signature Wave : " + e.getMessage(), 500, e);
        }
    }
}
