package dev.lions.unionflow.server.service;

import io.quarkus.oidc.runtime.OidcJwtCallerPrincipal;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.Set;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.jboss.logging.Logger;

/**
 * Service pour l'intégration avec Keycloak
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@ApplicationScoped
public class KeycloakService {

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

  @Inject SecurityIdentity securityIdentity;

  @Inject JsonWebToken jwt;

  /**
   * Vérifie si l'utilisateur actuel est authentifié
   *
   * @return true si l'utilisateur est authentifié
   */
  public boolean isAuthenticated() {
    return securityIdentity != null && !securityIdentity.isAnonymous();
  }

  /**
   * Obtient l'ID de l'utilisateur actuel depuis Keycloak
   *
   * @return l'ID de l'utilisateur ou null si non authentifié
   */
  public String getCurrentUserId() {
    if (!isAuthenticated()) {
      return null;
    }

    try {
      return jwt.getSubject();
    } catch (Exception e) {
      LOG.warnf("Erreur lors de la récupération de l'ID utilisateur: %s", e.getMessage());
      return null;
    }
  }

  /**
   * Obtient l'email de l'utilisateur actuel
   *
   * @return l'email de l'utilisateur ou null si non authentifié
   */
  public String getCurrentUserEmail() {
    if (!isAuthenticated()) {
      return null;
    }

    try {
      return jwt.getClaim("email");
    } catch (Exception e) {
      LOG.warnf("Erreur lors de la récupération de l'email utilisateur: %s", e.getMessage());
      return securityIdentity.getPrincipal().getName();
    }
  }

  /**
   * Obtient le nom complet de l'utilisateur actuel
   *
   * @return le nom complet ou null si non disponible
   */
  public String getCurrentUserFullName() {
    if (!isAuthenticated()) {
      return null;
    }

    try {
      String firstName = jwt.getClaim("given_name");
      String lastName = jwt.getClaim("family_name");

      if (firstName != null && lastName != null) {
        return firstName + " " + lastName;
      } else if (firstName != null) {
        return firstName;
      } else if (lastName != null) {
        return lastName;
      }

      // Fallback sur le nom d'utilisateur
      return jwt.getClaim("preferred_username");
    } catch (Exception e) {
      LOG.warnf("Erreur lors de la récupération du nom utilisateur: %s", e.getMessage());
      return null;
    }
  }

  /**
   * Obtient tous les rôles de l'utilisateur actuel
   *
   * @return les rôles de l'utilisateur
   */
  public Set<String> getCurrentUserRoles() {
    if (!isAuthenticated()) {
      return Set.of();
    }

    return securityIdentity.getRoles();
  }

  /**
   * Vérifie si l'utilisateur actuel a un rôle spécifique
   *
   * @param role le rôle à vérifier
   * @return true si l'utilisateur a le rôle
   */
  public boolean hasRole(String role) {
    if (!isAuthenticated()) {
      return false;
    }

    return securityIdentity.hasRole(role);
  }

  /**
   * Vérifie si l'utilisateur actuel a au moins un des rôles spécifiés
   *
   * @param roles les rôles à vérifier
   * @return true si l'utilisateur a au moins un des rôles
   */
  public boolean hasAnyRole(String... roles) {
    if (!isAuthenticated()) {
      return false;
    }

    for (String role : roles) {
      if (securityIdentity.hasRole(role)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Vérifie si l'utilisateur actuel a tous les rôles spécifiés
   *
   * @param roles les rôles à vérifier
   * @return true si l'utilisateur a tous les rôles
   */
  public boolean hasAllRoles(String... roles) {
    if (!isAuthenticated()) {
      return false;
    }

    for (String role : roles) {
      if (!securityIdentity.hasRole(role)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Obtient une claim spécifique du JWT
   *
   * @param claimName le nom de la claim
   * @return la valeur de la claim ou null si non trouvée
   */
  public <T> T getClaim(String claimName) {
    if (!isAuthenticated()) {
      return null;
    }

    try {
      return jwt.getClaim(claimName);
    } catch (Exception e) {
      LOG.warnf("Erreur lors de la récupération de la claim %s: %s", claimName, e.getMessage());
      return null;
    }
  }

  /**
   * Obtient toutes les claims du JWT
   *
   * @return toutes les claims ou une map vide si non authentifié
   */
  public Set<String> getAllClaimNames() {
    if (!isAuthenticated()) {
      return Set.of();
    }

    try {
      Set<String> names = jwt.getClaimNames();
      return names != null ? names : Set.of();
    } catch (Exception e) {
      LOG.warnf("Erreur lors de la récupération des claims: %s", e.getMessage());
      return Set.of();
    }
  }

  /**
   * Obtient les informations utilisateur pour les logs
   *
   * @return informations utilisateur formatées
   */
  public String getUserInfoForLogging() {
    if (!isAuthenticated()) {
      return "Utilisateur non authentifié";
    }

    String email = getCurrentUserEmail();
    String fullName = getCurrentUserFullName();
    Set<String> roles = getCurrentUserRoles();

    return String.format(
        "Utilisateur: %s (%s), Rôles: %s",
        fullName != null ? fullName : "N/A", email != null ? email : "N/A", roles);
  }

  /**
   * Vérifie si l'utilisateur actuel est un administrateur
   *
   * @return true si l'utilisateur est administrateur
   */
  public boolean isAdmin() {
    return hasRole("ADMIN") || hasRole("admin");
  }

  /**
   * Vérifie si l'utilisateur actuel peut gérer les membres
   *
   * @return true si l'utilisateur peut gérer les membres
   */
  public boolean canManageMembers() {
    return hasAnyRole(
        "ADMIN",
        "GESTIONNAIRE_MEMBRE",
        "PRESIDENT",
        "SECRETAIRE",
        "admin",
        "gestionnaire_membre",
        "president",
        "secretaire");
  }

  /**
   * Vérifie si l'utilisateur actuel peut gérer les finances
   *
   * @return true si l'utilisateur peut gérer les finances
   */
  public boolean canManageFinances() {
    return hasAnyRole("ADMIN", "TRESORIER", "PRESIDENT", "admin", "tresorier", "president");
  }

  /**
   * Vérifie si l'utilisateur actuel peut gérer les événements
   *
   * @return true si l'utilisateur peut gérer les événements
   */
  public boolean canManageEvents() {
    return hasAnyRole(
        "ADMIN",
        "ORGANISATEUR_EVENEMENT",
        "PRESIDENT",
        "SECRETAIRE",
        "admin",
        "organisateur_evenement",
        "president",
        "secretaire");
  }

  /**
   * Vérifie si l'utilisateur actuel peut gérer les organisations
   *
   * @return true si l'utilisateur peut gérer les organisations
   */
  public boolean canManageOrganizations() {
    return hasAnyRole("ADMIN", "PRESIDENT", "admin", "president");
  }

  /** Log les informations de sécurité pour debug */
  public void logSecurityInfo() {
    if (LOG.isDebugEnabled()) {
      LOG.debugf("Informations de sécurité: %s", getUserInfoForLogging());
    }
  }

  /**
   * Obtient le token d'accès brut
   *
   * @return le token JWT brut ou null si non disponible
   */
  public String getRawAccessToken() {
    if (!isAuthenticated()) {
      return null;
    }

    try {
      if (jwt instanceof OidcJwtCallerPrincipal) {
        return ((OidcJwtCallerPrincipal) jwt).getRawToken();
      }
      return jwt.getRawToken();
    } catch (Exception e) {
      LOG.warnf("Erreur lors de la récupération du token brut: %s", e.getMessage());
      return null;
    }
  }
}
