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

import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import dev.lions.unionflow.server.api.validation.ValidationConstants;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * DTO pour les propositions d'aide dans le système de solidarité
 *
 * <p>Ce DTO représente une proposition d'aide faite par un membre pour aider soit une demande
 * spécifique, soit de manière générale.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PropositionAideDTO {

  // === IDENTIFICATION ===

  /** Identifiant unique de la proposition d'aide */
  private String id;

  /** Numéro de référence de la proposition (généré automatiquement) */
  @Pattern(
      regexp = "^PA-\\d{4}-\\d{6}$",
      message = "Le numéro de référence doit suivre le format PA-YYYY-NNNNNN")
  private String numeroReference;

  // === INFORMATIONS DE BASE ===

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

  /** Titre de la proposition */
  @NotBlank(message = "Le titre est obligatoire")
  @Size(min = 10, max = 100, message = "Le titre doit contenir entre 10 et 100 caractères")
  private String titre;

  /** Description détaillée de l'aide proposée */
  @NotBlank(message = "La description est obligatoire")
  @Size(min = 20, max = 1000, message = "La description doit contenir entre 20 et 1000 caractères")
  private String description;

  /** Conditions ou critères pour bénéficier de l'aide */
  @Size(max = 500, message = "Les conditions ne peuvent pas dépasser 500 caractères")
  private String conditions;

  // === MONTANT ET CAPACITÉ ===

  /** Montant maximum que le proposant peut offrir (si applicable) */
  @DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif")
  @DecimalMax(value = "1000000.0", message = "Le montant ne peut pas dépasser 1 000 000 FCFA")
  @Digits(
      integer = ValidationConstants.MONTANT_INTEGER_DIGITS,
      fraction = ValidationConstants.MONTANT_FRACTION_DIGITS,
      message = ValidationConstants.MONTANT_DIGITS_MESSAGE)
  private BigDecimal montantMaximum;

  /** Nombre maximum de bénéficiaires */
  @Min(value = 1, message = "Le nombre de bénéficiaires doit être au moins 1")
  @Max(value = 100, message = "Le nombre de bénéficiaires ne peut pas dépasser 100")
  @Builder.Default
  private Integer nombreMaxBeneficiaires = 1;

  /** Devise du montant */
  @Builder.Default private String devise = "FCFA";

  // === ACTEURS ===

  /** Identifiant du proposant */
  @NotBlank(message = "L'identifiant du proposant est obligatoire")
  private String proposantId;

  /** Nom complet du proposant */
  private String proposantNom;

  /** Identifiant de l'organisation du proposant */
  @NotBlank(message = "L'identifiant de l'organisation est obligatoire")
  private String organisationId;

  /** Identifiant de la demande d'aide liée (si proposition spécifique) */
  private String demandeAideId;

  // === STATUT ET DISPONIBILITÉ ===

  /** Statut de la proposition */
  @NotNull(message = "Le statut est obligatoire")
  @Builder.Default
  private StatutProposition statut = StatutProposition.ACTIVE;

  /** Indique si la proposition est disponible */
  @Builder.Default private Boolean estDisponible = true;

  /** Indique si la proposition est récurrente */
  @Builder.Default private Boolean estRecurrente = false;

  /** Fréquence de récurrence (si applicable) */
  private String frequenceRecurrence;

  // === DATES ET DÉLAIS ===

  /** Date de création de la proposition */
  @NotNull(message = "La date de création est obligatoire")
  @Builder.Default
  private LocalDateTime dateCreation = LocalDateTime.now();

  /** Date d'expiration de la proposition */
  private LocalDateTime dateExpiration;

  /** Date de dernière modification */
  @Builder.Default private LocalDateTime dateModification = LocalDateTime.now();

  /** Délai de réponse souhaité en heures */
  @Min(value = 1, message = "Le délai de réponse doit être au moins 1 heure")
  @Max(value = 8760, message = "Le délai de réponse ne peut pas dépasser 1 an")
  @Builder.Default
  private Integer delaiReponseHeures = 72;

  // === CRITÈRES ET PRÉFÉRENCES ===

  /** Critères de sélection des bénéficiaires */
  private List<CritereSelectionDTO> criteresSelection;

  /** Zones géographiques couvertes */
  private List<String> zonesGeographiques;

  /** Groupes cibles (âge, situation, etc.) */
  private List<String> groupesCibles;

  /** Compétences ou ressources disponibles */
  private List<String> competencesRessources;

  // === CONTACT ET DISPONIBILITÉ ===

  /** Informations de contact préférées */
  private ContactProposantDTO contactProposant;

  /** Créneaux de disponibilité */
  private List<CreneauDisponibiliteDTO> creneauxDisponibilite;

  /** Mode de contact préféré */
  private String modeContactPrefere;

  // === HISTORIQUE ET SUIVI ===

  /** Nombre de demandes traitées avec cette proposition */
  @Builder.Default private Integer nombreDemandesTraitees = 0;

  /** Nombre de bénéficiaires aidés */
  @Builder.Default private Integer nombreBeneficiairesAides = 0;

  /** Montant total versé */
  @Builder.Default private Double montantTotalVerse = 0.0;

  /** Note moyenne des bénéficiaires */
  @DecimalMin(value = "0.0", message = "La note doit être positive")
  @DecimalMax(value = "5.0", message = "La note ne peut pas dépasser 5")
  private Double noteMoyenne;

  /** Nombre d'évaluations reçues */
  @Builder.Default private Integer nombreEvaluations = 0;

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

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

  /** Données personnalisées */
  private Map<String, Object> donneesPersonnalisees;

  /** Indique si la proposition est mise en avant */
  @Builder.Default private Boolean estMiseEnAvant = false;

  /** Score de pertinence calculé automatiquement */
  private Double scorePertinence;

  /** Nombre de vues de la proposition */
  @Builder.Default private Integer nombreVues = 0;

  /** Nombre de candidatures reçues */
  @Builder.Default private Integer nombreCandidatures = 0;

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

  /** Vérifie si la proposition est active et disponible */
  public boolean isActiveEtDisponible() {
    return statut == StatutProposition.ACTIVE && estDisponible && !isExpiree();
  }

  /** Vérifie si la proposition est expirée */
  public boolean isExpiree() {
    return dateExpiration != null && LocalDateTime.now().isAfter(dateExpiration);
  }

  /** Vérifie si la proposition peut encore accepter des bénéficiaires */
  public boolean peutAccepterBeneficiaires() {
    return isActiveEtDisponible() && nombreBeneficiairesAides < nombreMaxBeneficiaires;
  }

  /** Calcule le pourcentage de capacité utilisée */
  public double getPourcentageCapaciteUtilisee() {
    if (nombreMaxBeneficiaires == 0) return 100.0;
    return (nombreBeneficiairesAides * 100.0) / nombreMaxBeneficiaires;
  }

  /** Retourne le nombre de places restantes */
  public int getPlacesRestantes() {
    return Math.max(0, nombreMaxBeneficiaires - nombreBeneficiairesAides);
  }

  /** Vérifie si la proposition correspond à un type d'aide */
  public boolean correspondAuType(TypeAide type) {
    return typeAide == type
        || (typeAide.getCategorie().equals(type.getCategorie()) && typeAide != TypeAide.AUTRE);
  }

  /** Calcule le score de compatibilité avec une demande */
  public double getScoreCompatibilite(DemandeAideDTO demande) {
    double score = 0.0;

    // Correspondance exacte du type
    if (typeAide == demande.getTypeAide()) {
      score += 50.0;
    } else if (typeAide.getCategorie().equals(demande.getTypeAide().getCategorie())) {
      score += 30.0;
    }

    // Montant compatible
    if (montantMaximum != null && demande.getMontantDemande() != null) {
      if (demande.getMontantDemande().compareTo(montantMaximum) <= 0) {
        score += 20.0;
      } else {
        score -= 10.0;
      }
    }

    // Disponibilité
    if (peutAccepterBeneficiaires()) {
      score += 15.0;
    }

    // Réputation
    if (noteMoyenne != null && noteMoyenne >= 4.0) {
      score += 10.0;
    }

    // Récence
    long joursDepuisCreation =
        java.time.Duration.between(dateCreation, LocalDateTime.now()).toDays();
    if (joursDepuisCreation <= 30) {
      score += 5.0;
    }

    return Math.min(100.0, Math.max(0.0, score));
  }

  /** Énumération des statuts de proposition */
  public enum StatutProposition {
    BROUILLON("Brouillon"),
    ACTIVE("Active"),
    SUSPENDUE("Suspendue"),
    EXPIREE("Expirée"),
    TERMINEE("Terminée"),
    ANNULEE("Annulée");

    private final String libelle;

    StatutProposition(String libelle) {
      this.libelle = libelle;
    }

    public String getLibelle() {
      return libelle;
    }
  }
}
