package dev.lions.unionflow.server.entity;

import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
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_ville", columnList = "ville"),
      @Index(name = "idx_organisation_pays", columnList = "pays"),
      @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
  @Column(name = "adresse", length = 500)
  private String adresse;

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

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

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

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

  // 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
  @Column(name = "organisation_parente_id")
  private UUID organisationParenteId;

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

  // 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;

  // Relations
  @OneToMany(mappedBy = "organisation", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @Builder.Default
  private List<Membre> membres = 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);
  }

  /** Marque l'entité comme modifiée */
  public void marquerCommeModifie(String utilisateur) {
    this.setDateModification(LocalDateTime.now());
    this.setModifiePar(utilisateur);
    if (this.getVersion() != null) {
      this.setVersion(this.getVersion() + 1);
    } else {
      this.setVersion(1L);
    }
  }

  /** 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 (nombreMembres == null) {
      nombreMembres = 0;
    }
    if (nombreAdministrateurs == null) {
      nombreAdministrateurs = 0;
    }
    if (organisationPublique == null) {
      organisationPublique = true;
    }
    if (accepteNouveauxMembres == null) {
      accepteNouveauxMembres = true;
    }
    if (cotisationObligatoire == null) {
      cotisationObligatoire = false;
    }
  }
}
