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

import dev.lions.unionflow.server.entity.Devise;
import dev.lions.unionflow.server.payment.pispi.PispiAuth;
import dev.lions.unionflow.server.payment.pispi.PispiSignatureVerifier;
import dev.lions.unionflow.server.repository.TauxChangeRepository;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;

/**
 * Service de vérification des pré-requis PI-SPI BCEAO avant activation production.
 *
 * <p>Permet à l'équipe ops/compliance de valider en un coup d'œil que tous les facteurs
 * sont en place avant l'activation de l'intégration PI-SPI sandbox/production.
 *
 * <p>8 vérifications réalisées :
 * <ul>
 *   <li>OAuth2 client credentials (client_id + client_secret)</li>
 *   <li>X-API-Key API Business</li>
 *   <li>mTLS keystore présent (path + password)</li>
 *   <li>Truststore présent (optionnel mais recommandé)</li>
 *   <li>Webhook signature secret (HMAC-SHA256)</li>
 *   <li>Base URL configurée (sandbox vs production)</li>
 *   <li>Taux de change EUR/XOF récent (≤ 7 jours)</li>
 *   <li>Provider PI-SPI globalement configuré ({@link PispiAuth#isConfigured()})</li>
 * </ul>
 *
 * @since 2026-04-25 (P1-NEW-15)
 */
@ApplicationScoped
public class PispiReadinessService {

  private static final Logger LOG = Logger.getLogger(PispiReadinessService.class);

  @Inject PispiAuth pispiAuth;
  @Inject PispiSignatureVerifier signatureVerifier;
  @Inject TauxChangeRepository tauxChangeRepository;

  @ConfigProperty(name = "pispi.api.base-url",
      defaultValue = "https://sandbox.pispi.bceao.int/business-api/v1")
  String baseUrl;

  /**
   * Exécute tous les checks et retourne un rapport structuré.
   *
   * @return rapport synthèse + détail par check
   */
  public ReadinessReport verifierReadiness() {
    List<CheckResult> checks = new ArrayList<>();

    checks.add(verifierOAuth2());
    checks.add(verifierApiKey());
    checks.add(verifierMtlsKeystore());
    checks.add(verifierTruststore());
    checks.add(verifierWebhookSecret());
    checks.add(verifierBaseUrl());
    checks.add(verifierTauxEurXof());
    checks.add(verifierProviderConfigured());

    ReadinessStatus globalStatus = computeGlobalStatus(checks);
    List<String> blocking = checks.stream()
        .filter(c -> c.severity() == Severity.BLOCKING && c.status() == CheckStatus.FAIL)
        .map(c -> c.name() + " — " + c.message())
        .toList();
    List<String> warnings = checks.stream()
        .filter(c -> c.severity() == Severity.WARNING && c.status() == CheckStatus.FAIL)
        .map(c -> c.name() + " — " + c.message())
        .toList();

    LOG.infof("PI-SPI Readiness : %s — blocking=%d, warnings=%d",
        globalStatus, blocking.size(), warnings.size());
    return new ReadinessReport(globalStatus, baseUrl, checks, blocking, warnings);
  }

  // ────────────────────────────────────────────────────────────
  // Checks
  // ────────────────────────────────────────────────────────────

  CheckResult verifierOAuth2() {
    boolean ok = pispiAuth.hasClientId() && pispiAuth.hasClientSecret();
    return ok
        ? CheckResult.pass("OAUTH2_CREDENTIALS", Severity.BLOCKING,
            "client_id et client_secret configurés")
        : CheckResult.fail("OAUTH2_CREDENTIALS", Severity.BLOCKING,
            "Manquant : pispi.api.client-id et/ou pispi.api.client-secret");
  }

  CheckResult verifierApiKey() {
    return pispiAuth.hasApiKey()
        ? CheckResult.pass("API_KEY", Severity.BLOCKING,
            "X-API-Key configurée")
        : CheckResult.fail("API_KEY", Severity.BLOCKING,
            "Manquant : pispi.api.api-key (header X-API-Key obligatoire BCEAO)");
  }

  CheckResult verifierMtlsKeystore() {
    var pathOpt = pispiAuth.keystorePath();
    var pwdOpt = pispiAuth.keystorePassword();

    if (pathOpt.isEmpty() || pwdOpt.isEmpty()) {
      return CheckResult.fail("MTLS_KEYSTORE", Severity.BLOCKING,
          "Manquant : pispi.api.tls.keystore-path et/ou keystore-password (PKCS12 client cert)");
    }

    String path = pathOpt.get();
    if (!Files.exists(Path.of(path))) {
      return CheckResult.fail("MTLS_KEYSTORE", Severity.BLOCKING,
          "Keystore introuvable au chemin : " + path);
    }
    return CheckResult.pass("MTLS_KEYSTORE", Severity.BLOCKING,
        "Keystore PKCS12 présent : " + path);
  }

  CheckResult verifierTruststore() {
    var pathOpt = pispiAuth.truststorePath();
    if (pathOpt.isEmpty()) {
      return CheckResult.fail("MTLS_TRUSTSTORE", Severity.WARNING,
          "Truststore non configuré — fallback sur cacerts JVM (acceptable mais non recommandé)");
    }
    String path = pathOpt.get();
    if (!Files.exists(Path.of(path))) {
      return CheckResult.fail("MTLS_TRUSTSTORE", Severity.WARNING,
          "Truststore introuvable au chemin : " + path);
    }
    return CheckResult.pass("MTLS_TRUSTSTORE", Severity.WARNING,
        "Truststore présent : " + path);
  }

  CheckResult verifierWebhookSecret() {
    return signatureVerifier.hasWebhookSecret()
        ? CheckResult.pass("WEBHOOK_SECRET", Severity.BLOCKING,
            "Webhook HMAC secret configuré")
        : CheckResult.fail("WEBHOOK_SECRET", Severity.BLOCKING,
            "Manquant : pispi.webhook.secret (signature HMAC-SHA256 webhooks)");
  }

  CheckResult verifierBaseUrl() {
    if (baseUrl == null || baseUrl.isBlank()) {
      return CheckResult.fail("BASE_URL", Severity.BLOCKING,
          "Base URL PI-SPI non configurée");
    }
    boolean isSandbox = baseUrl.contains("sandbox") || baseUrl.contains("dev");
    String env = isSandbox ? "SANDBOX" : "PRODUCTION";
    return CheckResult.pass("BASE_URL", Severity.WARNING,
        "Base URL : " + baseUrl + " (" + env + ")");
  }

  CheckResult verifierTauxEurXof() {
    LocalDate dateLimite = LocalDate.now().minusDays(7);
    var taux = tauxChangeRepository.trouverPlusRecent(Devise.EUR, Devise.XOF, LocalDate.now());

    if (taux.isEmpty()) {
      return CheckResult.fail("TAUX_EUR_XOF", Severity.WARNING,
          "Aucun taux EUR/XOF disponible — conversion impossible (parité fixe BCEAO devrait être seedée)");
    }
    if (taux.get().getDateValidite().isBefore(dateLimite)) {
      return CheckResult.fail("TAUX_EUR_XOF", Severity.WARNING,
          "Taux EUR/XOF obsolète (" + taux.get().getDateValidite() + ") — synchroniser");
    }
    return CheckResult.pass("TAUX_EUR_XOF", Severity.WARNING,
        "Taux EUR/XOF récent : " + taux.get().getTaux() + " (date " + taux.get().getDateValidite() + ")");
  }

  CheckResult verifierProviderConfigured() {
    return pispiAuth.isConfigured()
        ? CheckResult.pass("PROVIDER_CONFIGURED", Severity.BLOCKING,
            "PispiAuth.isConfigured() = true — provider en mode RÉEL")
        : CheckResult.fail("PROVIDER_CONFIGURED", Severity.BLOCKING,
            "PispiAuth.isConfigured() = false — provider en mode MOCK (un facteur manque)");
  }

  // ────────────────────────────────────────────────────────────
  // Status global
  // ────────────────────────────────────────────────────────────

  private ReadinessStatus computeGlobalStatus(List<CheckResult> checks) {
    boolean anyBlockingFail = checks.stream()
        .anyMatch(c -> c.severity() == Severity.BLOCKING && c.status() == CheckStatus.FAIL);
    if (anyBlockingFail) return ReadinessStatus.BLOCKED;

    boolean anyWarning = checks.stream()
        .anyMatch(c -> c.severity() == Severity.WARNING && c.status() == CheckStatus.FAIL);
    if (anyWarning) return ReadinessStatus.DEGRADED;

    return ReadinessStatus.READY;
  }

  // ────────────────────────────────────────────────────────────
  // DTOs
  // ────────────────────────────────────────────────────────────

  public enum ReadinessStatus { READY, DEGRADED, BLOCKED }

  public enum CheckStatus { PASS, FAIL }

  public enum Severity { BLOCKING, WARNING }

  public record ReadinessReport(
      ReadinessStatus globalStatus,
      String baseUrl,
      List<CheckResult> checks,
      List<String> blockingIssues,
      List<String> warnings
  ) {}

  public record CheckResult(
      String name,
      CheckStatus status,
      Severity severity,
      String message
  ) {
    public static CheckResult pass(String name, Severity severity, String message) {
      return new CheckResult(name, CheckStatus.PASS, severity, message);
    }
    public static CheckResult fail(String name, Severity severity, String message) {
      return new CheckResult(name, CheckStatus.FAIL, severity, message);
    }
  }
}
