package dev.lions.unionflow.server.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * Entité Organisation avec UUID Représente une organisation (Lions Club,
 * Association,
 * Coopérative, etc.)
 *
 * @author UnionFlow Team
 * @version 2.0
 * @since 2025-01-16
 */
@Entity
@Table(name = "organisations", indexes = {
    @Index(name = "idx_organisation_nom", columnList = "nom"),
    @Index(name = "idx_organisation_email", columnList = "email", unique = true),
    @Index(name = "idx_organisation_statut", columnList = "statut"),
    @Index(name = "idx_organisation_type", columnList = "type_organisation"),
    @Index(name = "idx_organisation_parente", columnList = "organisation_parente_id"),
    @Index(name = "idx_organisation_numero_enregistrement", columnList = "numero_enregistrement", unique = true)
})
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class Organisation extends BaseEntity {

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

  @Column(name = "nom_court", length = 50)
  private String nomCourt;

  @NotBlank
  @Column(name = "type_organisation", nullable = false, length = 50)
  private String typeOrganisation;

  @NotBlank
  @Column(name = "statut", nullable = false, length = 50)
  private String statut;

  @Column(name = "description", length = 2000)
  private String description;

  @Column(name = "date_fondation")
  private LocalDate dateFondation;

  @Column(name = "numero_enregistrement", unique = true, length = 100)
  private String numeroEnregistrement;

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

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

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

  @Email
  @Column(name = "email_secondaire", length = 255)
  private String emailSecondaire;

  // Adresse principale (champs dénormalisés pour performance)
  @Column(name = "adresse", length = 500)
  private String adresse;

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

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

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

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

  // Coordonnées géographiques
  @DecimalMin(value = "-90.0", message = "La latitude doit être comprise entre -90 et 90")
  @DecimalMax(value = "90.0", message = "La latitude doit être comprise entre -90 et 90")
  @Digits(integer = 3, fraction = 6)
  @Column(name = "latitude", precision = 9, scale = 6)
  private BigDecimal latitude;

  @DecimalMin(value = "-180.0", message = "La longitude doit être comprise entre -180 et 180")
  @DecimalMax(value = "180.0", message = "La longitude doit être comprise entre -180 et 180")
  @Digits(integer = 3, fraction = 6)
  @Column(name = "longitude", precision = 9, scale = 6)
  private BigDecimal longitude;

  // Web et réseaux sociaux
  @Column(name = "site_web", length = 500)
  private String siteWeb;

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

  @Column(name = "reseaux_sociaux", length = 1000)
  private String reseauxSociaux;

  // ── Hiérarchie ──────────────────────────────────────────────────────────────

  /** Organisation parente — FK propre (null = organisation racine) */
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "organisation_parente_id")
  private Organisation organisationParente;

  @Builder.Default
  @Column(name = "niveau_hierarchique", nullable = false)
  private Integer niveauHierarchique = 0;

  /**
   * TRUE si c'est l'organisation racine qui porte la souscription SaaS
   * pour toute sa hiérarchie.
   */
  @Builder.Default
  @Column(name = "est_organisation_racine", nullable = false)
  private Boolean estOrganisationRacine = true;

  /**
   * Chemin hiérarchique complet — ex: /uuid-racine/uuid-intermediate/uuid-feuille
   * Permet des requêtes récursives optimisées sans CTE.
   */
  @Column(name = "chemin_hierarchique", length = 2000)
  private String cheminHierarchique;

  // Statistiques
  @Builder.Default
  @Column(name = "nombre_membres", nullable = false)
  private Integer nombreMembres = 0;

  @Builder.Default
  @Column(name = "nombre_administrateurs", nullable = false)
  private Integer nombreAdministrateurs = 0;

  // Finances
  @DecimalMin(value = "0.0", message = "Le budget annuel doit être positif")
  @Digits(integer = 12, fraction = 2)
  @Column(name = "budget_annuel", precision = 14, scale = 2)
  private BigDecimal budgetAnnuel;

  @Builder.Default
  @Column(name = "devise", length = 3)
  private String devise = "XOF";

  @Builder.Default
  @Column(name = "cotisation_obligatoire", nullable = false)
  private Boolean cotisationObligatoire = false;

  @DecimalMin(value = "0.0", message = "Le montant de cotisation doit être positif")
  @Digits(integer = 10, fraction = 2)
  @Column(name = "montant_cotisation_annuelle", precision = 12, scale = 2)
  private BigDecimal montantCotisationAnnuelle;

  // Informations complémentaires
  @Column(name = "objectifs", length = 2000)
  private String objectifs;

  @Column(name = "activites_principales", length = 2000)
  private String activitesPrincipales;

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

  @Column(name = "partenaires", length = 1000)
  private String partenaires;

  @Column(name = "notes", length = 1000)
  private String notes;

  // Paramètres
  @Builder.Default
  @Column(name = "organisation_publique", nullable = false)
  private Boolean organisationPublique = true;

  @Builder.Default
  @Column(name = "accepte_nouveaux_membres", nullable = false)
  private Boolean accepteNouveauxMembres = true;

  /** Catégorie du type d'organisation (ASSOCIATIF, FINANCIER_SOLIDAIRE, RELIGIEUX, PROFESSIONNEL, RESEAU_FEDERATION) */
  @Column(name = "categorie_type", length = 50)
  private String categorieType;

  /** ID de l'Organization Keycloak 26 correspondante — null si pas encore migrée. */
  @Column(name = "keycloak_org_id")
  private UUID keycloakOrgId;

  /** Modules activés pour cette organisation (liste CSV, ex: "MEMBRES,COTISATIONS,TONTINE") */
  @Column(name = "modules_actifs", length = 1000)
  private String modulesActifs;

  /**
   * Référentiel comptable applicable à cette organisation.
   *
   * <p>Détermine quel plan comptable est appliqué et quels états financiers sont générés
   * (bilan, compte de résultat, annexes). Mappage par défaut depuis {@code typeOrganisation}
   * via {@link ReferentielComptable#defaultFor(String)} ; l'admin peut overrider manuellement.
   *
   * @since 2026-04-25 — découverte SYCEBNL (11ᵉ Acte uniforme OHADA en vigueur 1er jan 2024)
   */
  @Enumerated(EnumType.STRING)
  @Column(name = "referentiel_comptable", nullable = false, length = 20)
  @Builder.Default
  private ReferentielComptable referentielComptable = ReferentielComptable.SYSCOHADA;

  /**
   * UUID du membre désigné comme Compliance Officer de l'organisation (rôle obligatoire selon
   * Instruction BCEAO 001-03-2025). Doit être rattaché à la direction générale, distinct du
   * trésorier (séparation des pouvoirs).
   *
   * @since 2026-04-25 — Instruction BCEAO 001-03-2025 (LBC/FT)
   */
  @Column(name = "compliance_officer_id")
  private UUID complianceOfficerId;

  // Relations

  /** Adhésions des membres à cette organisation */
  @JsonIgnore
  @OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @Builder.Default
  private List<MembreOrganisation> membresOrganisations = new ArrayList<>();

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

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

  /** Méthode métier pour obtenir le nom complet avec sigle */
  public String getNomComplet() {
    if (nomCourt != null && !nomCourt.isEmpty()) {
      return nom + " (" + nomCourt + ")";
    }
    return nom;
  }

  /** Méthode métier pour calculer l'ancienneté en années */
  public int getAncienneteAnnees() {
    if (dateFondation == null) {
      return 0;
    }
    return Period.between(dateFondation, LocalDate.now()).getYears();
  }

  /**
   * Méthode métier pour vérifier si l'organisation est récente (moins de 2 ans)
   */
  public boolean isRecente() {
    return getAncienneteAnnees() < 2;
  }

  /** Méthode métier pour vérifier si l'organisation est active */
  public boolean isActive() {
    return "ACTIVE".equals(statut) && Boolean.TRUE.equals(getActif());
  }

  /** Méthode métier pour ajouter un membre */
  public void ajouterMembre() {
    if (nombreMembres == null) {
      nombreMembres = 0;
    }
    nombreMembres++;
  }

  /** Méthode métier pour retirer un membre */
  public void retirerMembre() {
    if (nombreMembres != null && nombreMembres > 0) {
      nombreMembres--;
    }
  }

  /** Méthode métier pour activer l'organisation */
  public void activer(String utilisateur) {
    this.statut = "ACTIVE";
    this.setActif(true);
    marquerCommeModifie(utilisateur);
  }

  /** Méthode métier pour suspendre l'organisation */
  public void suspendre(String utilisateur) {
    this.statut = "SUSPENDUE";
    this.accepteNouveauxMembres = false;
    marquerCommeModifie(utilisateur);
  }

  /** Méthode métier pour dissoudre l'organisation */
  public void dissoudre(String utilisateur) {
    this.statut = "DISSOUTE";
    this.setActif(false);
    this.accepteNouveauxMembres = false;
    marquerCommeModifie(utilisateur);
  }

  /** Callback JPA avant la persistance */
  @PrePersist
  protected void onCreate() {
    super.onCreate(); // Appelle le onCreate de BaseEntity
    if (statut == null) {
      statut = "ACTIVE";
    }
    if (typeOrganisation == null) {
      typeOrganisation = "ASSOCIATION";
    }
    if (devise == null) {
      devise = "XOF";
    }
    if (niveauHierarchique == null) {
      niveauHierarchique = 0;
    }
    if (estOrganisationRacine == null) {
      estOrganisationRacine = (organisationParente == null);
    }
    if (nombreMembres == null) {
      nombreMembres = 0;
    }
    if (nombreAdministrateurs == null) {
      nombreAdministrateurs = 0;
    }
    if (organisationPublique == null) {
      organisationPublique = true;
    }
    if (accepteNouveauxMembres == null) {
      accepteNouveauxMembres = true;
    }
    if (cotisationObligatoire == null) {
      cotisationObligatoire = false;
    }
  }
}
