package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import dev.lions.unionflow.server.entity.Aide;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import jakarta.enterprise.context.ApplicationScoped;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Repository pour la gestion des demandes d'aide et de solidarité
 * Utilise Panache pour simplifier les opérations JPA
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@ApplicationScoped
public class AideRepository implements PanacheRepository<Aide> {

    /**
     * Trouve une aide par son numéro de référence
     * 
     * @param numeroReference le numéro de référence unique
     * @return Optional contenant l'aide si trouvée
     */
    public Optional<Aide> findByNumeroReference(String numeroReference) {
        return find("numeroReference = ?1", numeroReference).firstResultOptional();
    }

    /**
     * Trouve toutes les aides actives
     * 
     * @return liste des aides actives
     */
    public List<Aide> findAllActives() {
        return find("actif = true", Sort.by("dateCreation").descending()).list();
    }

    /**
     * Trouve les aides par statut
     * 
     * @param statut le statut recherché
     * @return liste des aides avec ce statut
     */
    public List<Aide> findByStatut(StatutAide statut) {
        return find("statut = ?1 and actif = true", Sort.by("dateCreation").descending(), statut).list();
    }

    /**
     * Trouve les aides par type
     * 
     * @param typeAide le type d'aide recherché
     * @return liste des aides de ce type
     */
    public List<Aide> findByTypeAide(TypeAide typeAide) {
        return find("typeAide = ?1 and actif = true", Sort.by("dateCreation").descending(), typeAide).list();
    }

    /**
     * Trouve les aides d'un membre demandeur
     * 
     * @param membreId identifiant du membre demandeur
     * @return liste des aides du membre
     */
    public List<Aide> findByMembreDemandeur(Long membreId) {
        return find("membreDemandeur.id = ?1 and actif = true", 
                   Sort.by("dateCreation").descending(), membreId).list();
    }

    /**
     * Trouve les aides d'une organisation
     * 
     * @param organisationId identifiant de l'organisation
     * @return liste des aides de l'organisation
     */
    public List<Aide> findByOrganisation(Long organisationId) {
        return find("organisation.id = ?1 and actif = true", 
                   Sort.by("dateCreation").descending(), organisationId).list();
    }

    /**
     * Trouve les aides par priorité
     * 
     * @param priorite la priorité recherchée
     * @return liste des aides avec cette priorité
     */
    public List<Aide> findByPriorite(String priorite) {
        return find("priorite = ?1 and actif = true", 
                   Sort.by("dateCreation").descending(), priorite).list();
    }

    /**
     * Trouve les aides urgentes en attente
     * 
     * @return liste des aides urgentes en attente
     */
    public List<Aide> findAidesUrgentesEnAttente() {
        return find("priorite = 'URGENTE' and statut = ?1 and actif = true", 
                   Sort.by("dateCreation").ascending(), StatutAide.EN_ATTENTE).list();
    }

    /**
     * Trouve les aides publiques (visibles par tous)
     * 
     * @param page pagination
     * @param sort tri
     * @return liste paginée des aides publiques
     */
    public List<Aide> findAidesPubliques(Page page, Sort sort) {
        return find("aidePublique = true and actif = true", sort).page(page).list();
    }

    /**
     * Trouve les aides en attente d'évaluation
     * 
     * @param page pagination
     * @param sort tri
     * @return liste paginée des aides en attente
     */
    public List<Aide> findAidesEnAttente(Page page, Sort sort) {
        return find("statut = ?1 and actif = true", sort, StatutAide.EN_ATTENTE).page(page).list();
    }

    /**
     * Trouve les aides approuvées non encore versées
     * 
     * @return liste des aides approuvées
     */
    public List<Aide> findAidesApprouveesNonVersees() {
        return find("statut = ?1 and actif = true", 
                   Sort.by("dateEvaluation").ascending(), StatutAide.APPROUVEE).list();
    }

    /**
     * Trouve les aides avec date limite proche
     * 
     * @param joursAvantLimite nombre de jours avant la limite
     * @return liste des aides avec date limite proche
     */
    public List<Aide> findAidesAvecDateLimiteProche(int joursAvantLimite) {
        LocalDate dateLimite = LocalDate.now().plusDays(joursAvantLimite);
        return find("dateLimite <= ?1 and statut = ?2 and actif = true", 
                   Sort.by("dateLimite").ascending(), dateLimite, StatutAide.EN_ATTENTE).list();
    }

    /**
     * Recherche textuelle dans les titres et descriptions
     * 
     * @param recherche terme de recherche
     * @param page pagination
     * @param sort tri
     * @return liste paginée des aides correspondantes
     */
    public List<Aide> rechercheTextuelle(String recherche, Page page, Sort sort) {
        String pattern = "%" + recherche.toLowerCase() + "%";
        return find("(lower(titre) like ?1 or lower(description) like ?1) and actif = true", 
                   sort, pattern).page(page).list();
    }

    /**
     * Recherche avancée avec filtres multiples
     * 
     * @param membreId identifiant du membre (optionnel)
     * @param organisationId identifiant de l'organisation (optionnel)
     * @param statut statut (optionnel)
     * @param typeAide type d'aide (optionnel)
     * @param priorite priorité (optionnel)
     * @param dateCreationMin date de création minimum (optionnel)
     * @param dateCreationMax date de création maximum (optionnel)
     * @param montantMin montant minimum (optionnel)
     * @param montantMax montant maximum (optionnel)
     * @param page pagination
     * @param sort tri
     * @return liste filtrée des aides
     */
    public List<Aide> rechercheAvancee(Long membreId, Long organisationId, StatutAide statut, 
                                     TypeAide typeAide, String priorite, LocalDate dateCreationMin, 
                                     LocalDate dateCreationMax, BigDecimal montantMin, 
                                     BigDecimal montantMax, Page page, Sort sort) {
        StringBuilder query = new StringBuilder("actif = true");
        Map<String, Object> params = new java.util.HashMap<>();
        
        if (membreId != null) {
            query.append(" and membreDemandeur.id = :membreId");
            params.put("membreId", membreId);
        }
        
        if (organisationId != null) {
            query.append(" and organisation.id = :organisationId");
            params.put("organisationId", organisationId);
        }
        
        if (statut != null) {
            query.append(" and statut = :statut");
            params.put("statut", statut);
        }
        
        if (typeAide != null) {
            query.append(" and typeAide = :typeAide");
            params.put("typeAide", typeAide);
        }
        
        if (priorite != null && !priorite.isEmpty()) {
            query.append(" and priorite = :priorite");
            params.put("priorite", priorite);
        }
        
        if (dateCreationMin != null) {
            query.append(" and date(dateCreation) >= :dateCreationMin");
            params.put("dateCreationMin", dateCreationMin);
        }
        
        if (dateCreationMax != null) {
            query.append(" and date(dateCreation) <= :dateCreationMax");
            params.put("dateCreationMax", dateCreationMax);
        }
        
        if (montantMin != null) {
            query.append(" and montantDemande >= :montantMin");
            params.put("montantMin", montantMin);
        }
        
        if (montantMax != null) {
            query.append(" and montantDemande <= :montantMax");
            params.put("montantMax", montantMax);
        }
        
        return find(query.toString(), sort, params).page(page).list();
    }

    /**
     * Compte les aides par statut
     * 
     * @param statut le statut
     * @return nombre d'aides avec ce statut
     */
    public long countByStatut(StatutAide statut) {
        return count("statut = ?1 and actif = true", statut);
    }

    /**
     * Compte les aides par type
     * 
     * @param typeAide le type d'aide
     * @return nombre d'aides de ce type
     */
    public long countByTypeAide(TypeAide typeAide) {
        return count("typeAide = ?1 and actif = true", typeAide);
    }

    /**
     * Compte les aides d'un membre
     * 
     * @param membreId identifiant du membre
     * @return nombre d'aides du membre
     */
    public long countByMembreDemandeur(Long membreId) {
        return count("membreDemandeur.id = ?1 and actif = true", membreId);
    }

    /**
     * Calcule le montant total demandé par statut
     * 
     * @param statut le statut
     * @return montant total demandé
     */
    public BigDecimal sumMontantDemandeByStatut(StatutAide statut) {
        BigDecimal result = find("select sum(a.montantDemande) from Aide a where a.statut = ?1 and a.actif = true", statut)
                .project(BigDecimal.class)
                .firstResult();
        return result != null ? result : BigDecimal.ZERO;
    }

    /**
     * Calcule le montant total versé
     *
     * @return montant total versé
     */
    public BigDecimal sumMontantVerse() {
        BigDecimal result = find("select sum(a.montantVerse) from Aide a where a.montantVerse is not null and a.actif = true")
                .project(BigDecimal.class)
                .firstResult();
        return result != null ? result : BigDecimal.ZERO;
    }

    /**
     * Trouve les aides nécessitant un suivi
     * (approuvées depuis plus de X jours sans versement)
     *
     * @param joursDepuisApprobation nombre de jours depuis l'approbation
     * @return liste des aides nécessitant un suivi
     */
    public List<Aide> findAidesNecessitantSuivi(int joursDepuisApprobation) {
        LocalDateTime dateLimit = LocalDateTime.now().minusDays(joursDepuisApprobation);
        return find("statut = ?1 and dateEvaluation <= ?2 and actif = true",
                   Sort.by("dateEvaluation").ascending(), StatutAide.APPROUVEE, dateLimit).list();
    }

    /**
     * Trouve les aides les plus consultées
     *
     * @param limite nombre maximum d'aides à retourner
     * @return liste des aides les plus consultées
     */
    public List<Aide> findAidesLesPlusConsultees(int limite) {
        return find("aidePublique = true and actif = true",
                   Sort.by("nombreVues").descending())
                   .page(Page.ofSize(limite))
                   .list();
    }

    /**
     * Trouve les aides récentes (créées dans les X derniers jours)
     *
     * @param nombreJours nombre de jours
     * @param page pagination
     * @param sort tri
     * @return liste paginée des aides récentes
     */
    public List<Aide> findAidesRecentes(int nombreJours, Page page, Sort sort) {
        LocalDateTime dateLimit = LocalDateTime.now().minusDays(nombreJours);
        return find("dateCreation >= ?1 and actif = true", sort, dateLimit).page(page).list();
    }

    /**
     * Statistiques globales des aides
     *
     * @return map contenant les statistiques
     */
    public Map<String, Object> getStatistiquesGlobales() {
        Map<String, Object> stats = new java.util.HashMap<>();

        // Compteurs par statut
        stats.put("total", count("actif = true"));
        stats.put("enAttente", count("statut = ?1 and actif = true", StatutAide.EN_ATTENTE));
        stats.put("enCours", count("statut = ?1 and actif = true", StatutAide.EN_COURS_EVALUATION));
        stats.put("approuvees", count("statut = ?1 and actif = true", StatutAide.APPROUVEE));
        stats.put("versees", count("statut = ?1 and actif = true", StatutAide.VERSEE));
        stats.put("rejetees", count("statut = ?1 and actif = true", StatutAide.REJETEE));
        stats.put("annulees", count("statut = ?1 and actif = true", StatutAide.ANNULEE));

        // Compteurs par priorité
        stats.put("urgentes", count("priorite = 'URGENTE' and actif = true"));
        stats.put("hautePriorite", count("priorite = 'HAUTE' and actif = true"));
        stats.put("prioriteNormale", count("priorite = 'NORMALE' and actif = true"));
        stats.put("bassePriorite", count("priorite = 'BASSE' and actif = true"));

        // Montants
        stats.put("montantTotalDemande", sumMontantDemandeByStatut(null));
        stats.put("montantTotalVerse", sumMontantVerse());
        stats.put("montantEnAttente", sumMontantDemandeByStatut(StatutAide.EN_ATTENTE));
        stats.put("montantApprouve", sumMontantDemandeByStatut(StatutAide.APPROUVEE));

        // Aides publiques vs privées
        stats.put("aidesPubliques", count("aidePublique = true and actif = true"));
        stats.put("aidesPrivees", count("aidePublique = false and actif = true"));
        stats.put("aidesAnonymes", count("aideAnonyme = true and actif = true"));

        return stats;
    }

    /**
     * Statistiques par période
     *
     * @param dateDebut date de début
     * @param dateFin date de fin
     * @return map contenant les statistiques de la période
     */
    public Map<String, Object> getStatistiquesPeriode(LocalDate dateDebut, LocalDate dateFin) {
        Map<String, Object> stats = new java.util.HashMap<>();

        LocalDateTime dateDebutTime = dateDebut.atStartOfDay();
        LocalDateTime dateFinTime = dateFin.atTime(23, 59, 59);

        String baseQuery = "dateCreation >= ?1 and dateCreation <= ?2 and actif = true";

        stats.put("totalPeriode", count(baseQuery, dateDebutTime, dateFinTime));
        stats.put("enAttentePeriode", count(baseQuery + " and statut = ?3",
                                          dateDebutTime, dateFinTime, StatutAide.EN_ATTENTE));
        stats.put("approuveesPeriode", count(baseQuery + " and statut = ?3",
                                           dateDebutTime, dateFinTime, StatutAide.APPROUVEE));
        stats.put("verseesPeriode", count(baseQuery + " and statut = ?3",
                                        dateDebutTime, dateFinTime, StatutAide.VERSEE));

        // Montant total demandé sur la période
        BigDecimal montantPeriode = find("select sum(a.montantDemande) from Aide a where " + baseQuery,
                                        dateDebutTime, dateFinTime)
                .project(BigDecimal.class)
                .firstResult();
        stats.put("montantTotalPeriode", montantPeriode != null ? montantPeriode : BigDecimal.ZERO);

        return stats;
    }

    /**
     * Trouve les aides par évaluateur
     *
     * @param evaluateurId identifiant de l'évaluateur
     * @param page pagination
     * @param sort tri
     * @return liste paginée des aides évaluées par ce membre
     */
    public List<Aide> findByEvaluateur(Long evaluateurId, Page page, Sort sort) {
        return find("evaluePar.id = ?1 and actif = true", sort, evaluateurId).page(page).list();
    }

    /**
     * Trouve les aides avec justificatifs manquants
     *
     * @return liste des aides sans justificatifs
     */
    public List<Aide> findAidesSansJustificatifs() {
        return find("justificatifsFournis = false and statut = ?1 and actif = true",
                   Sort.by("dateCreation").ascending(), StatutAide.EN_ATTENTE).list();
    }

    /**
     * Met à jour le nombre de vues d'une aide
     *
     * @param aideId identifiant de l'aide
     */
    public void incrementerNombreVues(Long aideId) {
        update("nombreVues = nombreVues + 1, dateModification = ?1 where id = ?2",
               LocalDateTime.now(), aideId);
    }
}
