package dev.lions.unionflow.server.api.dto.organisation;

import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.organisation.StatutOrganisation;
import dev.lions.unionflow.server.api.enums.organisation.TypeOrganisation;
import dev.lions.unionflow.server.api.validation.ValidationConstants;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.Period;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;

/**
 * DTO pour la gestion des organisations (Lions Club, Associations, Coopératives, etc.) Représente
 * une organisation avec ses informations complètes et sa hiérarchie
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-10
 */
@Getter
@Setter
public class OrganisationDTO extends BaseDTO {

  private static final long serialVersionUID = 1L;

  /** Nom de l'organisation */
  @NotBlank(message = "Le nom de l'organisation" + ValidationConstants.OBLIGATOIRE_MESSAGE)
  @Size(
      min = ValidationConstants.NOM_ORGANISATION_MIN_LENGTH,
      max = ValidationConstants.NOM_ORGANISATION_MAX_LENGTH,
      message = ValidationConstants.NOM_ORGANISATION_SIZE_MESSAGE)
  private String nom;

  /** Nom court ou sigle */
  @Size(
      max = ValidationConstants.NOM_COURT_MAX_LENGTH,
      message = ValidationConstants.NOM_COURT_SIZE_MESSAGE)
  private String nomCourt;

  /** Type d'organisation */
  @NotNull(message = "Le type d'organisation est obligatoire")
  private TypeOrganisation typeOrganisation;

  /** Statut de l'organisation */
  @NotNull(message = "Le statut de l'organisation est obligatoire")
  private StatutOrganisation statut;

  /** Description de l'organisation */
  @Size(
      max = ValidationConstants.DESCRIPTION_MAX_LENGTH,
      message = ValidationConstants.DESCRIPTION_SIZE_MESSAGE)
  private String description;

  /** Date de fondation */
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateFondation;

  /** Numéro d'enregistrement officiel */
  @Size(max = 100, message = "Le numéro d'enregistrement ne peut pas dépasser 100 caractères")
  private String numeroEnregistrement;

  /** Adresse complète */
  @Size(max = 500, message = "L'adresse ne peut pas dépasser 500 caractères")
  private String adresse;

  /** Ville */
  @Size(max = 100, message = "La ville ne peut pas dépasser 100 caractères")
  private String ville;

  /** Région/Province */
  @Size(max = 100, message = "La région ne peut pas dépasser 100 caractères")
  private String region;

  /** Pays */
  @Size(max = 100, message = "Le pays ne peut pas dépasser 100 caractères")
  private String pays;

  /** Code postal */
  @Pattern(regexp = "^[0-9A-Z\\-\\s]{3,10}$", message = "Format de code postal invalide")
  private String codePostal;

  /** Latitude pour géolocalisation */
  @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 = 2,
      fraction = 8,
      message = "La latitude ne peut avoir plus de 2 chiffres entiers et 8 décimales")
  private BigDecimal latitude;

  /** Longitude pour géolocalisation */
  @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 = 8,
      message = "La longitude ne peut avoir plus de 3 chiffres entiers et 8 décimales")
  private BigDecimal longitude;

  /** Téléphone principal */
  @Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$", message = "Format de téléphone invalide")
  private String telephone;

  /** Téléphone secondaire */
  @Pattern(regexp = "^\\+?[0-9\\s\\-\\(\\)]{8,20}$", message = "Format de téléphone invalide")
  private String telephoneSecondaire;

  /** Email principal */
  @Email(message = "Format d'email invalide")
  @Size(max = 200, message = "L'email ne peut pas dépasser 200 caractères")
  private String email;

  /** Email secondaire */
  @Email(message = "Format d'email invalide")
  @Size(max = 200, message = "L'email ne peut pas dépasser 200 caractères")
  private String emailSecondaire;

  /** Site web */
  @Pattern(
      regexp =
          "^https?://[\\w\\-]+(\\.[\\w\\-]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?$",
      message = "Format d'URL invalide")
  @Size(max = 500, message = "L'URL ne peut pas dépasser 500 caractères")
  private String siteWeb;

  /** Logo de l'organisation (URL ou nom de fichier) */
  @Size(max = 500, message = "Le logo ne peut pas dépasser 500 caractères")
  private String logo;

  /** Organisation parente (pour hiérarchie) */
  private UUID organisationParenteId;

  /** Nom de l'organisation parente */
  private String nomOrganisationParente;

  /** Niveau hiérarchique (0 = racine) */
  private Integer niveauHierarchique;

  /** Nombre de membres actifs */
  private Integer nombreMembres;

  /** Nombre d'administrateurs */
  private Integer nombreAdministrateurs;

  /** Budget annuel */
  @DecimalMin(value = "0.0", message = "Le budget doit être positif")
  @Digits(
      integer = 12,
      fraction = 2,
      message = "Le budget ne peut avoir plus de 12 chiffres entiers et 2 décimales")
  private BigDecimal budgetAnnuel;

  /** Devise du budget */
  @Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
  private String devise;

  /** Objectifs de l'organisation */
  @Size(max = 2000, message = "Les objectifs ne peuvent pas dépasser 2000 caractères")
  private String objectifs;

  /** Activités principales */
  @Size(max = 2000, message = "Les activités ne peuvent pas dépasser 2000 caractères")
  private String activitesPrincipales;

  /** Réseaux sociaux (JSON) */
  @Size(max = 1000, message = "Les réseaux sociaux ne peuvent pas dépasser 1000 caractères")
  private String reseauxSociaux;

  /** Certifications ou labels */
  @Size(max = 500, message = "Les certifications ne peuvent pas dépasser 500 caractères")
  private String certifications;

  /** Partenaires principaux */
  @Size(max = 1000, message = "Les partenaires ne peuvent pas dépasser 1000 caractères")
  private String partenaires;

  /** Notes administratives */
  @Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères")
  private String notes;

  /** Organisation publique (visible dans l'annuaire) */
  private Boolean organisationPublique;

  /** Accepte nouveaux membres */
  private Boolean accepteNouveauxMembres;

  /** Cotisation obligatoire */
  private Boolean cotisationObligatoire;

  /** Montant cotisation annuelle */
  @DecimalMin(value = "0.0", message = "Le montant de cotisation doit être positif")
  @Digits(
      integer = 10,
      fraction = 2,
      message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales")
  private BigDecimal montantCotisationAnnuelle;

  // Constructeurs
  public OrganisationDTO() {
    super();
    this.statut = StatutOrganisation.ACTIVE;
    this.typeOrganisation = TypeOrganisation.ASSOCIATION;
    this.devise = "XOF";
    this.niveauHierarchique = 0;
    this.nombreMembres = 0;
    this.nombreAdministrateurs = 0;
    this.organisationPublique = true;
    this.accepteNouveauxMembres = true;
    this.cotisationObligatoire = false;
  }

  public OrganisationDTO(String nom, TypeOrganisation type) {
    this();
    this.nom = nom;
    this.typeOrganisation = type;
  }

  // Méthodes utilitaires

  /**
   * Vérifie si l'organisation est active
   *
   * @return true si l'organisation est active
   */
  public boolean estActive() {
    return StatutOrganisation.ACTIVE.equals(statut);
  }

  /**
   * Vérifie si l'organisation est inactive
   *
   * @return true si l'organisation est inactive
   */
  public boolean estInactive() {
    return StatutOrganisation.INACTIVE.equals(statut);
  }

  /**
   * Vérifie si l'organisation est suspendue
   *
   * @return true si l'organisation est suspendue
   */
  public boolean estSuspendue() {
    return StatutOrganisation.SUSPENDUE.equals(statut);
  }

  /**
   * Vérifie si l'organisation est en cours de création
   *
   * @return true si l'organisation est en création
   */
  public boolean estEnCreation() {
    return StatutOrganisation.EN_CREATION.equals(statut);
  }

  /**
   * Vérifie si l'organisation est dissoute
   *
   * @return true si l'organisation est dissoute
   */
  public boolean estDissoute() {
    return StatutOrganisation.DISSOUTE.equals(statut);
  }

  /**
   * Calcule l'ancienneté de l'organisation en années
   *
   * @return L'ancienneté en années
   */
  public int getAncienneteAnnees() {
    if (dateFondation == null) return 0;
    return Period.between(dateFondation, LocalDate.now()).getYears();
  }

  /**
   * Calcule l'ancienneté de l'organisation en mois
   *
   * @return L'ancienneté en mois
   */
  public int getAncienneteMois() {
    if (dateFondation == null) return 0;
    Period periode = Period.between(dateFondation, LocalDate.now());
    return periode.getYears() * 12 + periode.getMonths();
  }

  /**
   * Vérifie si l'organisation a une géolocalisation
   *
   * @return true si latitude et longitude sont définies
   */
  public boolean possedGeolocalisation() {
    return latitude != null && longitude != null;
  }

  /**
   * Vérifie si l'organisation est une organisation racine (sans parent)
   *
   * @return true si l'organisation n'a pas de parent
   */
  public boolean estOrganisationRacine() {
    return organisationParenteId == null;
  }

  /**
   * Vérifie si l'organisation a des sous-organisations
   *
   * @return true si le niveau hiérarchique est supérieur à 0
   */
  public boolean possedeSousOrganisations() {
    return niveauHierarchique != null && niveauHierarchique > 0;
  }

  /**
   * Retourne le nom d'affichage (nom court si disponible, sinon nom complet)
   *
   * @return Le nom d'affichage
   */
  public String getNomAffichage() {
    return (nomCourt != null && !nomCourt.trim().isEmpty()) ? nomCourt : nom;
  }

  /**
   * Retourne l'adresse complète formatée
   *
   * @return L'adresse complète
   */
  public String getAdresseComplete() {
    StringBuilder sb = new StringBuilder();

    if (adresse != null && !adresse.trim().isEmpty()) {
      sb.append(adresse);
    }

    if (ville != null && !ville.trim().isEmpty()) {
      if (sb.length() > 0) sb.append(", ");
      sb.append(ville);
    }

    if (codePostal != null && !codePostal.trim().isEmpty()) {
      if (sb.length() > 0) sb.append(" ");
      sb.append(codePostal);
    }

    if (region != null && !region.trim().isEmpty()) {
      if (sb.length() > 0) sb.append(", ");
      sb.append(region);
    }

    if (pays != null && !pays.trim().isEmpty()) {
      if (sb.length() > 0) sb.append(", ");
      sb.append(pays);
    }

    return sb.toString();
  }

  /**
   * Calcule le ratio administrateurs/membres
   *
   * @return Le pourcentage d'administrateurs
   */
  public double getRatioAdministrateurs() {
    if (nombreMembres == null || nombreMembres == 0) return 0.0;
    if (nombreAdministrateurs == null) return 0.0;
    return (nombreAdministrateurs.doubleValue() / nombreMembres.doubleValue()) * 100.0;
  }

  /**
   * Vérifie si l'organisation a un budget défini
   *
   * @return true si un budget annuel est défini
   */
  public boolean hasBudget() {
    return budgetAnnuel != null && budgetAnnuel.compareTo(BigDecimal.ZERO) > 0;
  }

  /**
   * Active l'organisation
   *
   * @param utilisateur L'utilisateur qui active l'organisation
   */
  public void activer(String utilisateur) {
    this.statut = StatutOrganisation.ACTIVE;
    marquerCommeModifie(utilisateur);
  }

  /**
   * Suspend l'organisation
   *
   * @param utilisateur L'utilisateur qui suspend l'organisation
   */
  public void suspendre(String utilisateur) {
    this.statut = StatutOrganisation.SUSPENDUE;
    marquerCommeModifie(utilisateur);
  }

  /**
   * Dissout l'organisation
   *
   * @param utilisateur L'utilisateur qui dissout l'organisation
   */
  public void dissoudre(String utilisateur) {
    this.statut = StatutOrganisation.DISSOUTE;
    this.accepteNouveauxMembres = false;
    marquerCommeModifie(utilisateur);
  }

  /**
   * Désactive l'organisation
   *
   * @param utilisateur L'utilisateur qui désactive l'organisation
   */
  public void desactiver(String utilisateur) {
    this.statut = StatutOrganisation.INACTIVE;
    this.accepteNouveauxMembres = false;
    marquerCommeModifie(utilisateur);
  }

  /**
   * Met à jour le nombre de membres
   *
   * @param nouveauNombre Le nouveau nombre de membres
   * @param utilisateur L'utilisateur qui fait la mise à jour
   */
  public void mettreAJourNombreMembres(int nouveauNombre, String utilisateur) {
    this.nombreMembres = nouveauNombre;
    marquerCommeModifie(utilisateur);
  }

  /**
   * Ajoute un membre
   *
   * @param utilisateur L'utilisateur qui ajoute le membre
   */
  public void ajouterMembre(String utilisateur) {
    if (nombreMembres == null) nombreMembres = 0;
    nombreMembres++;
    marquerCommeModifie(utilisateur);
  }

  /**
   * Retire un membre
   *
   * @param utilisateur L'utilisateur qui retire le membre
   */
  public void retirerMembre(String utilisateur) {
    if (nombreMembres != null && nombreMembres > 0) {
      nombreMembres--;
      marquerCommeModifie(utilisateur);
    }
  }

  @Override
  public String toString() {
    return "OrganisationDTO{"
        + "nom='"
        + nom
        + '\''
        + ", nomCourt='"
        + nomCourt
        + '\''
        + ", typeOrganisation="
        + typeOrganisation
        + ", statut="
        + statut
        + ", ville='"
        + ville
        + '\''
        + ", pays='"
        + pays
        + '\''
        + ", nombreMembres="
        + nombreMembres
        + ", anciennete="
        + getAncienneteAnnees()
        + " ans"
        + "} "
        + super.toString();
  }

  // === MÉTHODES UTILITAIRES ===

  /** Retourne le libellé du statut */
  public String getStatutLibelle() {
    return statut != null ? statut.getLibelle() : "Non défini";
  }

  /** Retourne le libellé du type d'organisation */
  public String getTypeLibelle() {
    return typeOrganisation != null ? typeOrganisation.getLibelle() : "Non défini";
  }

  /** Ajoute un administrateur */
  public void ajouterAdministrateur(String utilisateur) {
    if (nombreAdministrateurs == null) nombreAdministrateurs = 0;
    nombreAdministrateurs++;
    marquerCommeModifie(utilisateur);
  }

  /** Retire un administrateur */
  public void retirerAdministrateur(String utilisateur) {
    if (nombreAdministrateurs != null && nombreAdministrateurs > 0) {
      nombreAdministrateurs--;
      marquerCommeModifie(utilisateur);
    }
  }
}
