package dev.lions.unionflow.server.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.*;

/**
 * Identité globale unique d'un utilisateur UnionFlow.
 *
 * <p>
 * Un utilisateur possède un seul compte sur toute la plateforme.
 * Ses adhésions aux organisations sont gérées dans {@link MembreOrganisation}.
 *
 * <p>
 * Table : {@code utilisateurs}
 */
@Entity
@Table(name = "utilisateurs", indexes = {
    @Index(name = "idx_utilisateur_email", columnList = "email", unique = true),
    @Index(name = "idx_utilisateur_numero", columnList = "numero_membre", unique = true),
    @Index(name = "idx_utilisateur_keycloak", columnList = "keycloak_id", unique = true),
    @Index(name = "idx_utilisateur_actif", columnList = "actif"),
    @Index(name = "idx_utilisateur_statut", columnList = "statut_compte")
})
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class Membre extends BaseEntity {

  /** Identifiant Keycloak (UUID du compte OIDC) */
  @Column(name = "keycloak_id", unique = true)
  private UUID keycloakId;

  /** Numéro de membre — unique globalement sur toute la plateforme */
  @NotBlank
  @Column(name = "numero_membre", unique = true, nullable = false, length = 20)
  private String numeroMembre;

  @NotBlank
  @Column(name = "prenom", nullable = false, length = 100)
  private String prenom;

  @NotBlank
  @Column(name = "nom", nullable = false, length = 100)
  private String nom;

  @Email
  @NotBlank
  @Column(name = "email", unique = true, nullable = false, length = 255)
  private String email;

  @Column(name = "telephone", length = 20)
  private String telephone;

  /** Token FCM pour les notifications push Firebase. NULL si l'app mobile n'est pas installée ou si le membre a refusé les notifications. */
  @Column(name = "fcm_token", length = 500)
  private String fcmToken;

  /**
   * Numéro CMU (Couverture Maladie Universelle) Côte d'Ivoire — auto-déclaré par le membre.
   *
   * <p>Obligatoire pour les organisations de type {@code MUTUELLE_SANTE} (Loi 2014-131
   * exige enrôlement CMU comme préalable à toute mutuelle complémentaire). Format CNAM :
   * 11 caractères alphanumériques. La vérification de la validité se fait manuellement
   * (admin) faute d'API publique CNAM disponible au 2026-04-25.
   *
   * @since 2026-04-25 — passage CMU à cotisation obligatoire 1er jan 2026
   */
  @Pattern(regexp = "^[A-Z0-9]{11}$|^$", message = "Le numéro CMU doit faire 11 caractères alphanumériques majuscules")
  @Column(name = "numero_cmu", length = 11)
  private String numeroCMU;

  /**
   * Pays de résidence (ISO-3, ex: FRA, USA, CAN). Différent de {@code nationalite} :
   * un Ivoirien (CIV) résidant en France a paysResidence=FRA. NULL ou CIV = résident UEMOA.
   *
   * @since 2026-04-25 (P2-NEW-7)
   */
  @Pattern(regexp = "^[A-Z]{3}$|^$", message = "Pays résidence doit être un code ISO-3")
  @Column(name = "pays_residence", length = 3)
  private String paysResidence;

  /** Numéro de passeport pour non-résidents (CNI insuffisante hors UEMOA). */
  @Column(name = "numero_passeport", length = 50)
  private String numeroPasseport;

  /** NIF/SSN/SIN — reporting fiscal accord bilatéral CI ↔ pays résidence. */
  @Column(name = "numero_fiscal_etranger", length = 50)
  private String numeroFiscalEtranger;

  /** TRUE si le membre est diaspora (résidence ≠ UEMOA). */
  @Builder.Default
  @Column(name = "est_diaspora", nullable = false)
  private Boolean estDiaspora = false;

  /** Devise préférée pour affichages et notifications (XOF par défaut). */
  @Builder.Default
  @Column(name = "devise_preferee", nullable = false, length = 3)
  private String devisePreferee = "XOF";

  @Pattern(regexp = "^\\+[1-9][0-9]{6,14}$", message = "Le numéro Wave doit être au format international E.164 (ex: +22507XXXXXXXX)")
  @Column(name = "telephone_wave", length = 20)
  private String telephoneWave;

  @NotNull
  @Column(name = "date_naissance", nullable = false)
  private LocalDate dateNaissance;

  @Column(name = "profession", length = 100)
  private String profession;

  @Column(name = "photo_url", length = 500)
  private String photoUrl;

  @Builder.Default
  @Column(name = "statut_compte", nullable = false, length = 30)
  private String statutCompte = "EN_ATTENTE_VALIDATION";

  /** Vrai si le membre n'a jamais changé son mot de passe généré par l'admin. */
  @Builder.Default
  @Column(name = "premiere_connexion", nullable = false)
  private Boolean premiereConnexion = true;

  /**
   * Statut matrimonial (domaine
   * {@code STATUT_MATRIMONIAL} dans
   * {@code types_reference}).
   */
  @Column(name = "statut_matrimonial", length = 50)
  private String statutMatrimonial;

  /** Nationalité. */
  @Column(name = "nationalite", length = 100)
  private String nationalite;

  /**
   * Type de pièce d'identité (domaine
   * {@code TYPE_IDENTITE} dans
   * {@code types_reference}).
   */
  @Column(name = "type_identite", length = 50)
  private String typeIdentite;

  /** Numéro de la pièce d'identité. */
  @Column(name = "numero_identite", length = 100)
  private String numeroIdentite;

  /** Notes / biographie libre du membre. */
  @Column(name = "notes", length = 1000)
  private String notes;

  /** Niveau de vigilance KYC LCB-FT (SIMPLIFIE, RENFORCE). */
  @Column(name = "niveau_vigilance_kyc", length = 20)
  private String niveauVigilanceKyc;

  /** Statut de vérification d'identité (NON_VERIFIE, EN_COURS, VERIFIE, REFUSE). */
  @Column(name = "statut_kyc", length = 20)
  private String statutKyc;

  /** Date de dernière vérification d'identité. */
  @Column(name = "date_verification_identite")
  private LocalDate dateVerificationIdentite;

  // ── Relations ────────────────────────────────────────────────────────────

  /** Adhésions à des organisations — CascadeType.REMOVE exclu intentionnellement pour conserver l'historique */
  @JsonIgnore
  @OneToMany(mappedBy = "membre", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
  @Builder.Default
  private List<MembreOrganisation> membresOrganisations = new ArrayList<>();

  @JsonIgnore
  @OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @Builder.Default
  private List<Adresse> adresses = new ArrayList<>();

  @JsonIgnore
  @OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @Builder.Default
  private List<CompteWave> comptesWave = new ArrayList<>();

  @JsonIgnore
  @OneToMany(mappedBy = "membre", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @Builder.Default
  private List<Paiement> paiements = new ArrayList<>();

  // ── Méthodes métier ───────────────────────────────────────────────────────

  public String getNomComplet() {
    return (prenom != null ? prenom : "") + " " + (nom != null ? nom : "");
  }

  public boolean isMajeur() {
    return dateNaissance != null && dateNaissance.isBefore(LocalDate.now().minusYears(18));
  }

  public int getAge() {
    return dateNaissance != null ? LocalDate.now().getYear() - dateNaissance.getYear() : 0;
  }

  @PrePersist
  protected void onCreate() {
    super.onCreate();
    if (statutCompte == null) {
      statutCompte = "EN_ATTENTE_VALIDATION";
    }
  }
}
