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

import jakarta.validation.constraints.*;
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 l'évaluation d'une aide reçue ou fournie
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class EvaluationAideDTO {

  /** Identifiant unique de l'évaluation */
  private String id;

  /** Identifiant de la demande d'aide évaluée */
  @NotBlank(message = "L'identifiant de la demande d'aide est obligatoire")
  private String demandeAideId;

  /** Identifiant de la proposition d'aide évaluée (si applicable) */
  private String propositionAideId;

  /** Identifiant de l'évaluateur */
  @NotBlank(message = "L'identifiant de l'évaluateur est obligatoire")
  private String evaluateurId;

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

  /** Rôle de l'évaluateur (beneficiaire, proposant, evaluateur_externe) */
  @NotBlank(message = "Le rôle de l'évaluateur est obligatoire")
  private String roleEvaluateur;

  /** Type d'évaluation */
  @NotNull(message = "Le type d'évaluation est obligatoire")
  @Builder.Default
  private TypeEvaluation typeEvaluation = TypeEvaluation.SATISFACTION_BENEFICIAIRE;

  /** Note globale (1-5) */
  @NotNull(message = "La note globale est obligatoire")
  @DecimalMin(value = "1.0", message = "La note doit être au moins 1")
  @DecimalMax(value = "5.0", message = "La note ne peut pas dépasser 5")
  private Double noteGlobale;

  /** Notes détaillées par critère */
  private Map<String, Double> notesDetaillees;

  /** Commentaire principal */
  @Size(min = 10, max = 1000, message = "Le commentaire doit contenir entre 10 et 1000 caractères")
  private String commentairePrincipal;

  /** Points positifs */
  @Size(max = 500, message = "Les points positifs ne peuvent pas dépasser 500 caractères")
  private String pointsPositifs;

  /** Points d'amélioration */
  @Size(max = 500, message = "Les points d'amélioration ne peuvent pas dépasser 500 caractères")
  private String pointsAmelioration;

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

  /** Indique si l'évaluateur recommande cette aide/proposant */
  @Builder.Default private Boolean recommande = true;

  /** Indique si l'aide a été utile */
  @Builder.Default private Boolean aideUtile = true;

  /** Indique si l'aide a résolu le problème */
  @Builder.Default private Boolean problemeResolu = true;

  /** Délai de réponse perçu (1=très lent, 5=très rapide) */
  @DecimalMin(value = "1.0", message = "La note délai doit être au moins 1")
  @DecimalMax(value = "5.0", message = "La note délai ne peut pas dépasser 5")
  private Double noteDelaiReponse;

  /** Qualité de la communication (1=très mauvaise, 5=excellente) */
  @DecimalMin(value = "1.0", message = "La note communication doit être au moins 1")
  @DecimalMax(value = "5.0", message = "La note communication ne peut pas dépasser 5")
  private Double noteCommunication;

  /** Professionnalisme (1=très mauvais, 5=excellent) */
  @DecimalMin(value = "1.0", message = "La note professionnalisme doit être au moins 1")
  @DecimalMax(value = "5.0", message = "La note professionnalisme ne peut pas dépasser 5")
  private Double noteProfessionnalisme;

  /** Respect des engagements (1=très mauvais, 5=excellent) */
  @DecimalMin(value = "1.0", message = "La note engagement doit être au moins 1")
  @DecimalMax(value = "5.0", message = "La note engagement ne peut pas dépasser 5")
  private Double noteRespectEngagements;

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

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

  /** Indique si l'évaluation est publique */
  @Builder.Default private Boolean estPublique = true;

  /** Indique si l'évaluation est anonyme */
  @Builder.Default private Boolean estAnonyme = false;

  /** Indique si l'évaluation a été vérifiée */
  @Builder.Default private Boolean estVerifiee = false;

  /** Date de vérification */
  private LocalDateTime dateVerification;

  /** Identifiant du vérificateur */
  private String verificateurId;

  /** Pièces jointes à l'évaluation (photos, documents) */
  private List<PieceJustificativeDTO> piecesJointes;

  /** Tags associés à l'évaluation */
  private List<String> tags;

  /** Données additionnelles */
  private Map<String, Object> donneesAdditionnelles;

  /** Nombre de personnes qui ont trouvé cette évaluation utile */
  @Builder.Default private Integer nombreUtile = 0;

  /** Nombre de signalements de cette évaluation */
  @Builder.Default private Integer nombreSignalements = 0;

  /** Statut de l'évaluation */
  @NotNull(message = "Le statut est obligatoire")
  @Builder.Default
  private StatutEvaluation statut = StatutEvaluation.ACTIVE;

  /** Énumération des types d'évaluation */
  public enum TypeEvaluation {
    SATISFACTION_BENEFICIAIRE("Satisfaction du bénéficiaire"),
    EVALUATION_PROPOSANT("Évaluation du proposant"),
    EVALUATION_PROCESSUS("Évaluation du processus"),
    SUIVI_POST_AIDE("Suivi post-aide"),
    EVALUATION_IMPACT("Évaluation d'impact"),
    RETOUR_EXPERIENCE("Retour d'expérience");

    private final String libelle;

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

    public String getLibelle() {
      return libelle;
    }
  }

  /** Énumération des statuts d'évaluation */
  public enum StatutEvaluation {
    BROUILLON("Brouillon"),
    ACTIVE("Active"),
    MASQUEE("Masquée"),
    SIGNALEE("Signalée"),
    SUPPRIMEE("Supprimée");

    private final String libelle;

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

    public String getLibelle() {
      return libelle;
    }
  }

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

  /** Calcule la note moyenne des critères détaillés */
  public Double getNoteMoyenneDetaillees() {
    if (notesDetaillees == null || notesDetaillees.isEmpty()) {
      return noteGlobale;
    }

    return notesDetaillees.values().stream()
        .mapToDouble(Double::doubleValue)
        .average()
        .orElse(noteGlobale);
  }

  /** Vérifie si l'évaluation est positive (note >= 4) */
  public boolean isPositive() {
    return noteGlobale != null && noteGlobale >= 4.0;
  }

  /** Vérifie si l'évaluation est négative (note <= 2) */
  public boolean isNegative() {
    return noteGlobale != null && noteGlobale <= 2.0;
  }

  /** Calcule un score de qualité global */
  public double getScoreQualite() {
    double score = noteGlobale != null ? noteGlobale : 0.0;

    // Bonus pour les notes détaillées
    if (noteDelaiReponse != null) score += noteDelaiReponse * 0.1;
    if (noteCommunication != null) score += noteCommunication * 0.1;
    if (noteProfessionnalisme != null) score += noteProfessionnalisme * 0.1;
    if (noteRespectEngagements != null) score += noteRespectEngagements * 0.1;

    // Bonus pour recommandation
    if (recommande != null && recommande) score += 0.2;

    // Bonus pour résolution du problème
    if (problemeResolu != null && problemeResolu) score += 0.3;

    // Malus pour signalements
    if (nombreSignalements > 0) score -= nombreSignalements * 0.1;

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

  /** Vérifie si l'évaluation est complète */
  public boolean isComplete() {
    return noteGlobale != null
        && commentairePrincipal != null
        && !commentairePrincipal.trim().isEmpty()
        && recommande != null
        && aideUtile != null
        && problemeResolu != null;
  }

  /** Retourne le niveau de satisfaction */
  public String getNiveauSatisfaction() {
    if (noteGlobale == null) return "Non évalué";

    return switch (noteGlobale.intValue()) {
      case 5 -> "Excellent";
      case 4 -> "Très bien";
      case 3 -> "Bien";
      case 2 -> "Passable";
      case 1 -> "Insuffisant";
      default -> "Non évalué";
    };
  }
}
