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

import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import dev.lions.unionflow.server.api.validation.ValidationConstants;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
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.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

/**
 * DTO unifié pour les demandes d'aide dans le système de solidarité
 *
 * <p>Ce DTO représente une demande d'aide complète avec toutes les informations nécessaires pour le
 * traitement, l'évaluation et le suivi. Remplace l'ancien AideDTO pour une approche unifiée.
 *
 * @author UnionFlow Team
 * @version 2.0
 * @since 2025-01-16
 */
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
public class DemandeAideDTO extends BaseDTO {

  private static final long serialVersionUID = 1L;

  // === IDENTIFICATION ===

  // ID hérité de BaseDTO

  /** Numéro de référence de la demande (généré automatiquement) */
  @Pattern(
      regexp = ValidationConstants.REFERENCE_AIDE_PATTERN,
      message = ValidationConstants.REFERENCE_AIDE_MESSAGE)
  private String numeroReference;

  // === INFORMATIONS DE BASE ===

  /** Type d'aide demandée */
  @NotNull(message = "Le type d'aide est obligatoire.")
  private TypeAide typeAide;

  /** Titre court de la demande */
  @NotBlank(message = "Le titre" + ValidationConstants.OBLIGATOIRE_MESSAGE)
  @Size(
      min = ValidationConstants.TITRE_MIN_LENGTH,
      max = ValidationConstants.TITRE_MAX_LENGTH,
      message = ValidationConstants.TITRE_SIZE_MESSAGE)
  private String titre;

  /** Description détaillée de la demande */
  @NotBlank(message = "La description" + ValidationConstants.OBLIGATOIRE_MESSAGE)
  @Size(
      min = ValidationConstants.DESCRIPTION_MIN_LENGTH,
      max = ValidationConstants.DESCRIPTION_MAX_LENGTH,
      message = ValidationConstants.DESCRIPTION_SIZE_MESSAGE)
  private String description;

  /** Justification de la demande */
  @Size(
      max = ValidationConstants.JUSTIFICATION_MAX_LENGTH,
      message = ValidationConstants.JUSTIFICATION_SIZE_MESSAGE)
  private String justification;

  // === MONTANT ET FINANCES ===

  /** Montant demandé (si applicable) */
  @DecimalMin(
      value = ValidationConstants.MONTANT_MIN_VALUE,
      inclusive = false,
      message = ValidationConstants.MONTANT_POSITIF_MESSAGE)
  @Digits(
      integer = ValidationConstants.MONTANT_INTEGER_DIGITS,
      fraction = ValidationConstants.MONTANT_FRACTION_DIGITS,
      message = ValidationConstants.MONTANT_DIGITS_MESSAGE)
  private BigDecimal montantDemande;

  /** Montant approuvé (si différent du montant demandé) */
  @DecimalMin(
      value = ValidationConstants.MONTANT_MIN_VALUE,
      inclusive = false,
      message = ValidationConstants.MONTANT_POSITIF_MESSAGE)
  @Digits(
      integer = ValidationConstants.MONTANT_INTEGER_DIGITS,
      fraction = ValidationConstants.MONTANT_FRACTION_DIGITS,
      message = ValidationConstants.MONTANT_DIGITS_MESSAGE)
  private BigDecimal montantApprouve;

  /** Montant versé effectivement */
  @DecimalMin(
      value = ValidationConstants.MONTANT_MIN_VALUE,
      inclusive = false,
      message = ValidationConstants.MONTANT_POSITIF_MESSAGE)
  @Digits(
      integer = ValidationConstants.MONTANT_INTEGER_DIGITS,
      fraction = ValidationConstants.MONTANT_FRACTION_DIGITS,
      message = ValidationConstants.MONTANT_DIGITS_MESSAGE)
  private BigDecimal montantVerse;

  /** Devise du montant (code ISO 3 lettres) */
  @Pattern(
      regexp = ValidationConstants.DEVISE_PATTERN,
      message = ValidationConstants.DEVISE_MESSAGE)
  private String devise = "XOF";

  // === ACTEURS ===

  /** Identifiant du demandeur (UUID) */
  @NotNull(message = "L'identifiant du demandeur est obligatoire")
  private UUID membreDemandeurId;

  /** Nom complet du demandeur */
  private String nomDemandeur;

  /** Numéro de membre du demandeur */
  private String numeroMembreDemandeur;

  /** Identifiant de l'évaluateur assigné */
  private String evaluateurId;

  /** Nom de l'évaluateur */
  private String evaluateurNom;

  /** Identifiant de l'approbateur */
  private String approvateurId;

  /** Nom de l'approbateur */
  private String approvateurNom;

  /** Identifiant de l'organisation (UUID) */
  @NotNull(message = "L'identifiant de l'organisation est obligatoire")
  private UUID associationId;

  /** Nom de l'association */
  private String nomAssociation;

  // === STATUT ET PRIORITÉ ===

  /** Statut actuel de la demande */
  @NotNull(message = "Le statut est obligatoire")
  private StatutAide statut = StatutAide.BROUILLON;

  /** Priorité de la demande */
  @NotNull(message = "La priorité est obligatoire")
  private PrioriteAide priorite = PrioriteAide.NORMALE;

  /** Motif de rejet (si applicable) */
  @Size(max = 500, message = "Le motif de rejet ne peut pas dépasser 500 caractères")
  private String motifRejet;

  /** Commentaires de l'évaluateur */
  @Size(max = 1000, message = "Les commentaires ne peuvent pas dépasser 1000 caractères")
  private String commentairesEvaluateur;

  // === DATES ===

  // Date de création héritée de BaseDTO

  /** Date de soumission de la demande */
  private LocalDateTime dateSoumission;

  /** Date limite de traitement */
  private LocalDateTime dateLimiteTraitement;

  /** Date d'évaluation */
  private LocalDateTime dateEvaluation;

  /** Date d'approbation */
  private LocalDateTime dateApprobation;

  /** Date de versement */
  private LocalDateTime dateVersement;

  /** Date de clôture */
  private LocalDateTime dateCloture;

  // Date de modification héritée de BaseDTO

  // === INFORMATIONS COMPLÉMENTAIRES ===

  /** Pièces justificatives attachées */
  private List<PieceJustificativeDTO> piecesJustificatives;

  /** Bénéficiaires de l'aide (si différents du demandeur) */
  private List<BeneficiaireAideDTO> beneficiaires;

  /** Historique des changements de statut */
  private List<HistoriqueStatutDTO> historiqueStatuts;

  /** Commentaires et échanges */
  private List<CommentaireAideDTO> commentaires;

  /** Données personnalisées spécifiques au type d'aide */
  private Map<String, Object> donneesPersonnalisees;

  /** Tags pour catégorisation */
  private List<String> tags;

  // === MÉTADONNÉES ===

  /** Indique si la demande est confidentielle */
  private Boolean estConfidentielle = false;

  /** Indique si la demande nécessite un suivi */
  private Boolean necessiteSuivi = false;

  /** Score de priorité calculé automatiquement */
  private Double scorePriorite;

  /** Nombre de vues de la demande */
  private Integer nombreVues = 0;

  // Version héritée de BaseDTO

  /** Informations de géolocalisation (si pertinent) */
  private LocalisationDTO localisation;

  /** Informations de contact d'urgence */
  private ContactUrgenceDTO contactUrgence;

  // === CHAMPS ADDITIONNELS D'AIDE ===

  /** Date limite pour l'aide */
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateLimite;

  /** Justificatifs fournis */
  private Boolean justificatifsFournis = false;

  /** Liste des documents joints (noms de fichiers) */
  @Size(max = 1000, message = "La liste des documents ne peut pas dépasser 1000 caractères")
  private String documentsJoints;

  /** Date de début de l'aide */
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateDebutAide;

  /** Date de fin de l'aide */
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateFinAide;

  /** Identifiant du membre aidant */
  private UUID membreAidantId;

  /** Nom du membre aidant */
  private String nomAidant;

  /** Mode de versement */
  @Size(max = 50, message = "Le mode de versement ne peut pas dépasser 50 caractères")
  private String modeVersement;

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

  /** Identifiant de celui qui a rejeté */
  private UUID rejeteParId;

  /** Nom de celui qui a rejeté */
  private String rejetePar;

  /** Date de rejet */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateRejet;

  /** Raison du rejet (si applicable) */
  @Size(max = 500, message = "La raison du rejet ne peut pas dépasser 500 caractères")
  private String raisonRejet;

  // === CONSTRUCTEURS ===

  /** Constructeur par défaut */
  public DemandeAideDTO() {
    super(); // Appelle le constructeur de BaseDTO qui génère l'UUID
    this.statut = StatutAide.EN_ATTENTE;
    this.priorite = PrioriteAide.NORMALE;
    this.devise = "XOF";
    this.nombreVues = 0;
    this.numeroReference = genererNumeroReference();
  }

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

  /** Vérifie si la demande est modifiable */
  public boolean estModifiable() {
    return statut != null && statut.permetModification();
  }

  /** Vérifie si la demande peut être annulée */
  public boolean peutEtreAnnulee() {
    return statut != null && statut.permetAnnulation();
  }

  /** Vérifie si la demande est urgente */
  public boolean estUrgente() {
    return priorite != null && priorite.isUrgente();
  }

  /** Vérifie si la demande est terminée */
  public boolean estTerminee() {
    return statut != null && statut.isEstFinal();
  }

  /** Vérifie si la demande est en succès */
  public boolean estEnSucces() {
    return statut != null && statut.isSucces();
  }

  /** Calcule le pourcentage d'avancement */
  public double getPourcentageAvancement() {
    if (statut == null) {
      return 0.0;
    }

    return switch (statut) {
      case BROUILLON -> 5.0;
      case SOUMISE -> 10.0;
      case EN_ATTENTE -> 20.0;
      case EN_COURS_EVALUATION -> 40.0;
      case INFORMATIONS_REQUISES -> 35.0;
      case APPROUVEE, APPROUVEE_PARTIELLEMENT -> 60.0;
      case EN_COURS_TRAITEMENT -> 70.0;
      case EN_COURS_VERSEMENT -> 85.0;
      case VERSEE, LIVREE, TERMINEE -> 100.0;
      case REJETEE, ANNULEE, EXPIREE -> 100.0;
      case SUSPENDUE -> 50.0;
      case EN_SUIVI -> 95.0;
      case CLOTUREE -> 100.0;
    };
  }

  /** Retourne le délai restant en heures */
  public long getDelaiRestantHeures() {
    if (dateLimiteTraitement == null) {
      return -1;
    }

    LocalDateTime maintenant = LocalDateTime.now();
    if (maintenant.isAfter(dateLimiteTraitement)) {
      return 0;
    }

    return java.time.Duration.between(maintenant, dateLimiteTraitement).toHours();
  }

  /** Vérifie si le délai est dépassé */
  public boolean estDelaiDepasse() {
    return getDelaiRestantHeures() == 0;
  }

  /** Retourne la durée de traitement en jours */
  public long getDureeTraitementJours() {
    if (dateCreation == null) {
      return 0;
    }

    LocalDateTime dateFin = dateCloture != null ? dateCloture : LocalDateTime.now();
    return java.time.Duration.between(dateCreation, dateFin).toDays();
  }

  // === MÉTHODES MÉTIER D'AIDE ===

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

  /** Retourne le libellé de la priorité */
  public String getPrioriteLibelle() {
    return priorite != null ? priorite.getLibelle() : "Normale";
  }

  /** Approuve la demande d'aide */
  public void approuver(
      UUID evaluateurId, String nomEvaluateur, BigDecimal montantApprouve, String commentaires) {
    this.statut = StatutAide.APPROUVEE;
    this.evaluateurId = evaluateurId.toString();
    this.evaluateurNom = nomEvaluateur;
    this.montantApprouve = montantApprouve;
    this.commentairesEvaluateur = commentaires;
    this.dateEvaluation = LocalDateTime.now();
    this.dateApprobation = LocalDateTime.now();
    marquerCommeModifie(nomEvaluateur);
  }

  /** Rejette la demande d'aide */
  public void rejeter(UUID evaluateurId, String nomEvaluateur, String raison) {
    this.statut = StatutAide.REJETEE;
    this.rejeteParId = evaluateurId;
    this.rejetePar = nomEvaluateur;
    this.raisonRejet = raison;
    this.dateRejet = LocalDateTime.now();
    this.dateEvaluation = LocalDateTime.now();
    marquerCommeModifie(nomEvaluateur);
  }

  /** Démarre l'aide */
  public void demarrerAide(UUID aidantId, String nomAidant) {
    this.statut = StatutAide.EN_COURS_TRAITEMENT;
    this.membreAidantId = aidantId;
    this.nomAidant = nomAidant;
    this.dateDebutAide = LocalDate.now();
    marquerCommeModifie(nomAidant);
  }

  /** Termine l'aide avec versement */
  public void terminerAvecVersement(
      BigDecimal montantVerse, String modeVersement, String numeroTransaction) {
    this.statut = StatutAide.TERMINEE;
    this.montantVerse = montantVerse;
    this.modeVersement = modeVersement;
    this.numeroTransaction = numeroTransaction;
    this.dateVersement = LocalDateTime.now();
    this.dateFinAide = LocalDate.now();
    marquerCommeModifie("SYSTEM");
  }

  /** Incrémente le nombre de vues */
  public void incrementerVues() {
    if (nombreVues == null) {
      nombreVues = 1;
    } else {
      nombreVues++;
    }
  }

  /** Génère un numéro de référence unique */
  public static String genererNumeroReference() {
    return "DA-"
        + LocalDate.now().getYear()
        + "-"
        + String.format("%06d", (int) (Math.random() * 1000000));
  }

  // === GETTERS EXPLICITES POUR COMPATIBILITÉ ===

  /** Retourne le type d'aide demandée */
  public TypeAide getTypeAide() {
    return typeAide;
  }

  /** Retourne le montant demandé */
  public BigDecimal getMontantDemande() {
    return montantDemande;
  }

  /** Marque comme modifié */
  public void marquerCommeModifie(String utilisateur) {
    LocalDateTime maintenant = LocalDateTime.now();
    this.dateModification = maintenant;
    super.marquerCommeModifie(utilisateur);
  }
}
