package dev.lions.unionflow.server.service.pep;

import jakarta.enterprise.context.ApplicationScoped;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

/**
 * Implémentation par défaut de {@link PepScreeningProvider} basée sur une liste interne.
 *
 * <p>À utiliser <strong>uniquement en mode dev / fallback</strong>. En production, remplacer
 * par un fournisseur commercial via {@code @Alternative @Priority}.
 *
 * <p>La liste interne contient quelques PEP UEMOA notoires (élus, hauts fonctionnaires) à titre
 * d'exemple. Le maintenir en mode prod n'est pas réaliste — aller chez Youverify / ComplyAdvantage.
 *
 * @since 2026-04-25 (P1-NEW-11)
 */
@ApplicationScoped
public class InMemoryPepScreeningProvider implements PepScreeningProvider {

  /** Seuil de similarité Levenshtein normalisé pour matcher. */
  private static final double SIMILARITY_THRESHOLD = 0.80;

  /** Liste interne minimale (à maintenir manuellement — non exhaustive). */
  private final List<PepMatch> staticList = new ArrayList<>();

  public InMemoryPepScreeningProvider() {
    // Seed minimal pour test (à compléter via configuration externe en prod).
    // Note : ces données sont fictives à titre de démonstration. Aucune assertion sur des
    // personnes réelles. La vraie liste doit venir d'un fournisseur licencié.
    staticList.add(new PepMatch(
        "EXEMPLE Demo Pep", "Exemple Fonction Publique", "CIV",
        LocalDate.of(2020, 1, 1), null,
        "MINISTER", "InMemoryPepScreeningProvider (DEV ONLY)", 1.0));
  }

  @Override
  public PepScreeningResult screen(String nom, String prenoms, LocalDate dateNaissance,
                                    String nationalite) {
    if (nom == null || nom.isBlank() || prenoms == null || prenoms.isBlank()) {
      return PepScreeningResult.notFound(providerName());
    }
    String requete = (nom + " " + prenoms).toLowerCase(Locale.ROOT).trim();

    List<PepMatch> matches = new ArrayList<>();
    for (PepMatch candidate : staticList) {
      String candidateName = candidate.nomComplet().toLowerCase(Locale.ROOT);
      double similarity = computeSimilarity(requete, candidateName);
      if (similarity >= SIMILARITY_THRESHOLD) {
        if (nationalite == null
            || candidate.pays() == null
            || candidate.pays().equalsIgnoreCase(nationalite)) {
          matches.add(new PepMatch(
              candidate.nomComplet(), candidate.fonction(), candidate.pays(),
              candidate.dateDebutFonction(), candidate.dateFinFonction(),
              candidate.categorie(), candidate.source(), similarity));
        }
      }
    }
    matches.sort(Comparator.comparingDouble(PepMatch::scoreSimilarite).reversed());

    String warning = matches.isEmpty()
        ? null
        : "Provider InMemory : liste non exhaustive — résultat indicatif uniquement";

    return new PepScreeningResult(!matches.isEmpty(), matches, providerName(), warning);
  }

  @Override
  public String providerName() {
    return "InMemory";
  }

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

  /**
   * Similarité Jaro-Winkler simplifiée (1 - Levenshtein normalisé). Pour un screening
   * banking-grade, utiliser une vraie lib (Apache Commons Text JaroWinklerDistance).
   */
  static double computeSimilarity(String s1, String s2) {
    int max = Math.max(s1.length(), s2.length());
    if (max == 0) return 0.0;
    if (s1.equals(s2)) return 1.0;
    int distance = levenshtein(s1, s2);
    return 1.0 - ((double) distance / max);
  }

  private static int levenshtein(String s1, String s2) {
    int[][] dp = new int[s1.length() + 1][s2.length() + 1];
    for (int i = 0; i <= s1.length(); i++) dp[i][0] = i;
    for (int j = 0; j <= s2.length(); j++) dp[0][j] = j;
    for (int i = 1; i <= s1.length(); i++) {
      for (int j = 1; j <= s2.length(); j++) {
        int cost = (s1.charAt(i - 1) == s2.charAt(j - 1)) ? 0 : 1;
        dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + cost);
      }
    }
    return dp[s1.length()][s2.length()];
  }
}
