package dev.lions.unionflow.server.entity;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

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;

/**
 * Entité Organisation avec Lombok
 * Représente une organisation (Lions Club, Association, Coopérative, etc.)
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@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 = false)
public class Organisation extends PanacheEntity {

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

    // Métadonnées
    @Builder.Default
    @Column(name = "actif", nullable = false)
    private Boolean actif = true;

    @Builder.Default
    @Column(name = "date_creation", nullable = false)
    private LocalDateTime dateCreation = LocalDateTime.now();

    @Column(name = "date_modification")
    private LocalDateTime dateModification;

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

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

    @Builder.Default
    @Column(name = "version", nullable = false)
    private Long version = 0L;

    // 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) && actif;
    }

    /**
     * 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.actif = 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.actif = false;
        this.accepteNouveauxMembres = false;
        marquerCommeModifie(utilisateur);
    }

    /**
     * Marque l'entité comme modifiée
     */
    public void marquerCommeModifie(String utilisateur) {
        this.dateModification = LocalDateTime.now();
        this.modifiePar = utilisateur;
        this.version++;
    }

    /**
     * Callback JPA avant la persistance
     */
    @PrePersist
    protected void onCreate() {
        if (dateCreation == null) {
            dateCreation = LocalDateTime.now();
        }
        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;
        }
        if (actif == null) {
            actif = true;
        }
        if (version == null) {
            version = 0L;
        }
    }

    /**
     * Callback JPA avant la mise à jour
     */
    @PreUpdate
    protected void onUpdate() {
        dateModification = LocalDateTime.now();
    }
}
