package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import lombok.*;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * Entité JPA pour la gestion des demandes d'aide et de solidarité
 * Représente les demandes d'assistance mutuelle entre membres
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@Entity
@Table(name = "aides", indexes = {
    @Index(name = "idx_aide_numero_reference", columnList = "numero_reference", unique = true),
    @Index(name = "idx_aide_membre_demandeur", columnList = "membre_demandeur_id"),
    @Index(name = "idx_aide_organisation", columnList = "organisation_id"),
    @Index(name = "idx_aide_statut", columnList = "statut"),
    @Index(name = "idx_aide_type", columnList = "type_aide"),
    @Index(name = "idx_aide_priorite", columnList = "priorite"),
    @Index(name = "idx_aide_date_creation", columnList = "date_creation"),
    @Index(name = "idx_aide_actif", columnList = "actif")
})
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = false)
public class Aide extends PanacheEntity {

    /** Numéro de référence unique de la demande (format: AIDE-YYYY-XXXXXX) */
    @NotBlank(message = "Le numéro de référence est obligatoire")
    @Pattern(regexp = "^AIDE-\\d{4}-[A-Z0-9]{6}$", 
             message = "Format de référence invalide (AIDE-YYYY-XXXXXX)")
    @Column(name = "numero_reference", unique = true, nullable = false, length = 20)
    private String numeroReference;

    /** Membre demandeur de l'aide */
    @NotNull(message = "Le membre demandeur est obligatoire")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "membre_demandeur_id", nullable = false)
    private Membre membreDemandeur;

    /** Organisation à laquelle appartient la demande */
    @NotNull(message = "L'organisation est obligatoire")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "organisation_id", nullable = false)
    private Organisation organisation;

    /** Type d'aide demandée */
    @NotNull(message = "Le type d'aide est obligatoire")
    @Enumerated(EnumType.STRING)
    @Column(name = "type_aide", nullable = false, length = 30)
    private TypeAide typeAide;

    /** Titre de la demande d'aide */
    @NotBlank(message = "Le titre est obligatoire")
    @Size(min = 5, max = 200, message = "Le titre doit contenir entre 5 et 200 caractères")
    @Column(name = "titre", nullable = false, length = 200)
    private String titre;

    /** Description détaillée de la demande */
    @NotBlank(message = "La description est obligatoire")
    @Size(min = 20, max = 2000, message = "La description doit contenir entre 20 et 2000 caractères")
    @Column(name = "description", nullable = false, columnDefinition = "TEXT")
    private String description;

    /** Montant demandé */
    @DecimalMin(value = "0.0", inclusive = false, message = "Le montant demandé doit être positif")
    @Digits(integer = 12, fraction = 2, message = "Format de montant invalide")
    @Column(name = "montant_demande", precision = 15, scale = 2)
    private BigDecimal montantDemande;

    /** Montant approuvé par l'organisation */
    @DecimalMin(value = "0.0", inclusive = false, message = "Le montant approuvé doit être positif")
    @Digits(integer = 12, fraction = 2, message = "Format de montant invalide")
    @Column(name = "montant_approuve", precision = 15, scale = 2)
    private BigDecimal montantApprouve;

    /** Montant effectivement versé */
    @DecimalMin(value = "0.0", inclusive = false, message = "Le montant versé doit être positif")
    @Digits(integer = 12, fraction = 2, message = "Format de montant invalide")
    @Column(name = "montant_verse", precision = 15, scale = 2)
    private BigDecimal montantVerse;

    /** Devise du montant (par défaut XOF) */
    @Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
    @Builder.Default
    @Column(name = "devise", length = 3)
    private String devise = "XOF";

    /** Statut de la demande */
    @NotNull(message = "Le statut est obligatoire")
    @Enumerated(EnumType.STRING)
    @Builder.Default
    @Column(name = "statut", nullable = false, length = 30)
    private StatutAide statut = StatutAide.EN_ATTENTE;

    /** Priorité de la demande */
    @NotBlank(message = "La priorité est obligatoire")
    @Pattern(regexp = "^(BASSE|NORMALE|HAUTE|URGENTE)$", 
             message = "La priorité doit être BASSE, NORMALE, HAUTE ou URGENTE")
    @Builder.Default
    @Column(name = "priorite", nullable = false, length = 10)
    private String priorite = "NORMALE";

    /** Date limite pour la demande */
    @Column(name = "date_limite")
    private LocalDate dateLimite;

    /** Date de début de l'aide (si approuvée) */
    @Column(name = "date_debut_aide")
    private LocalDate dateDebutAide;

    /** Date de fin de l'aide */
    @Column(name = "date_fin_aide")
    private LocalDate dateFinAide;

    /** Justificatifs fournis */
    @Builder.Default
    @Column(name = "justificatifs_fournis", nullable = false)
    private Boolean justificatifsFournis = false;

    /** Documents joints (URLs ou chemins) */
    @Size(max = 1000, message = "Les documents joints ne peuvent pas dépasser 1000 caractères")
    @Column(name = "documents_joints", columnDefinition = "TEXT")
    private String documentsJoints;

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

    /** Membre qui a évalué la demande */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "evalue_par_id")
    private Membre evaluePar;

    /** Date d'évaluation */
    @Column(name = "date_evaluation")
    private LocalDateTime dateEvaluation;

    /** Mode de versement (ESPECES, VIREMENT, MOBILE_MONEY, CHEQUE) */
    @Pattern(regexp = "^(ESPECES|VIREMENT|MOBILE_MONEY|CHEQUE|AUTRE)$", 
             message = "Mode de versement invalide")
    @Column(name = "mode_versement", length = 20)
    private String modeVersement;

    /** Numéro de transaction (pour les paiements mobiles) */
    @Size(max = 50, message = "Le numéro de transaction ne peut pas dépasser 50 caractères")
    @Column(name = "numero_transaction", length = 50)
    private String numeroTransaction;

    /** Date de versement */
    @Column(name = "date_versement")
    private LocalDateTime dateVersement;

    /** Commentaires du bénéficiaire */
    @Size(max = 1000, message = "Les commentaires ne peuvent pas dépasser 1000 caractères")
    @Column(name = "commentaires_beneficiaire", columnDefinition = "TEXT")
    private String commentairesBeneficiaire;

    /** Note de satisfaction (1-5) */
    @Min(value = 1, message = "La note de satisfaction doit être entre 1 et 5")
    @Max(value = 5, message = "La note de satisfaction doit être entre 1 et 5")
    @Column(name = "note_satisfaction")
    private Integer noteSatisfaction;

    /** Aide publique (visible par tous les membres) */
    @Builder.Default
    @Column(name = "aide_publique", nullable = false)
    private Boolean aidePublique = true;

    /** Aide anonyme (demandeur anonyme) */
    @Builder.Default
    @Column(name = "aide_anonyme", nullable = false)
    private Boolean aideAnonyme = false;

    /** Nombre de vues de la demande */
    @Builder.Default
    @Column(name = "nombre_vues", nullable = false)
    private Integer nombreVues = 0;

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

    /** Date de rejet */
    @Column(name = "date_rejet")
    private LocalDateTime dateRejet;

    /** Membre qui a rejeté la demande */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "rejete_par_id")
    private Membre rejetePar;

    // Champs d'audit
    @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;

    @Version
    @Column(name = "version")
    private Long version;

    // ===== MÉTHODES MÉTIER =====

    /**
     * Génère un numéro de référence unique pour la demande d'aide
     * Format: AIDE-YYYY-XXXXXX
     */
    public static String genererNumeroReference() {
        return "AIDE-" + LocalDate.now().getYear() + "-" +
               String.format("%06d", (int) (Math.random() * 1000000));
    }

    /**
     * Approuve la demande d'aide avec un montant spécifique
     *
     * @param montantApprouve Montant approuvé
     * @param evaluateur Membre qui évalue
     * @param commentaires Commentaires d'évaluation
     */
    public void approuver(BigDecimal montantApprouve, Membre evaluateur, String commentaires) {
        this.statut = StatutAide.APPROUVEE;
        this.montantApprouve = montantApprouve;
        this.evaluePar = evaluateur;
        this.commentairesEvaluateur = commentaires;
        this.dateEvaluation = LocalDateTime.now();
        this.dateDebutAide = LocalDate.now();
        this.dateModification = LocalDateTime.now();
    }

    /**
     * Rejette la demande d'aide
     *
     * @param raison Raison du rejet
     * @param evaluateur Membre qui rejette
     */
    public void rejeter(String raison, Membre evaluateur) {
        this.statut = StatutAide.REJETEE;
        this.raisonRejet = raison;
        this.rejetePar = evaluateur;
        this.dateRejet = LocalDateTime.now();
        this.dateEvaluation = LocalDateTime.now();
        this.dateModification = LocalDateTime.now();
    }

    /**
     * Marque l'aide comme versée
     *
     * @param montantVerse Montant effectivement versé
     * @param modeVersement Mode de versement
     * @param numeroTransaction Numéro de transaction
     */
    public void marquerCommeVersee(BigDecimal montantVerse, String modeVersement, String numeroTransaction) {
        this.statut = StatutAide.VERSEE;
        this.montantVerse = montantVerse;
        this.modeVersement = modeVersement;
        this.numeroTransaction = numeroTransaction;
        this.dateVersement = LocalDateTime.now();
        this.dateFinAide = LocalDate.now();
        this.dateModification = LocalDateTime.now();
    }

    /**
     * Incrémente le nombre de vues de la demande
     */
    public void incrementerVues() {
        if (this.nombreVues == null) {
            this.nombreVues = 1;
        } else {
            this.nombreVues++;
        }
        this.dateModification = LocalDateTime.now();
    }

    /**
     * Vérifie si la demande est en cours de traitement
     */
    public boolean isEnCoursDeTraitement() {
        return this.statut == StatutAide.EN_COURS_EVALUATION ||
               this.statut == StatutAide.EN_COURS_VERSEMENT;
    }

    /**
     * Vérifie si la demande est terminée (versée ou rejetée)
     */
    public boolean isTerminee() {
        return this.statut == StatutAide.VERSEE ||
               this.statut == StatutAide.REJETEE ||
               this.statut == StatutAide.ANNULEE;
    }

    /**
     * Vérifie si la demande peut être modifiée
     */
    public boolean isPeutEtreModifiee() {
        return this.statut == StatutAide.EN_ATTENTE;
    }

    /**
     * Calcule le pourcentage d'aide accordée par rapport à la demande
     */
    public double getPourcentageAideAccordee() {
        if (montantDemande == null || montantDemande.compareTo(BigDecimal.ZERO) == 0) {
            return 0.0;
        }
        if (montantApprouve == null) {
            return 0.0;
        }
        return montantApprouve.divide(montantDemande, 4, java.math.RoundingMode.HALF_UP)
                             .multiply(BigDecimal.valueOf(100))
                             .doubleValue();
    }

    /**
     * Retourne le nom complet du demandeur (si pas anonyme)
     */
    public String getNomDemandeur() {
        if (aideAnonyme != null && aideAnonyme) {
            return "Demandeur anonyme";
        }
        return membreDemandeur != null ? membreDemandeur.getNomComplet() : "Inconnu";
    }

    // ===== CALLBACKS JPA =====

    @PrePersist
    public void prePersist() {
        if (numeroReference == null || numeroReference.isEmpty()) {
            numeroReference = genererNumeroReference();
        }
        if (dateCreation == null) {
            dateCreation = LocalDateTime.now();
        }
    }

    @PreUpdate
    public void preUpdate() {
        this.dateModification = LocalDateTime.now();
    }

    @Override
    public String toString() {
        return "Aide{" +
                "id=" + id +
                ", numeroReference='" + numeroReference + '\'' +
                ", titre='" + titre + '\'' +
                ", typeAide=" + typeAide +
                ", statut=" + statut +
                ", montantDemande=" + montantDemande +
                ", devise='" + devise + '\'' +
                ", priorite='" + priorite + '\'' +
                '}';
    }
}
