package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.Organisation;
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.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

/**
 * Repository pour l'entité Organisation
 * Utilise Panache pour simplifier les opérations JPA
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@ApplicationScoped
public class OrganisationRepository implements PanacheRepository<Organisation> {

    /**
     * Trouve une organisation par son email
     * 
     * @param email l'email de l'organisation
     * @return Optional contenant l'organisation si trouvée
     */
    public Optional<Organisation> findByEmail(String email) {
        return find("email = ?1", email).firstResultOptional();
    }

    /**
     * Trouve une organisation par son nom
     * 
     * @param nom le nom de l'organisation
     * @return Optional contenant l'organisation si trouvée
     */
    public Optional<Organisation> findByNom(String nom) {
        return find("nom = ?1", nom).firstResultOptional();
    }

    /**
     * Trouve une organisation par son numéro d'enregistrement
     * 
     * @param numeroEnregistrement le numéro d'enregistrement officiel
     * @return Optional contenant l'organisation si trouvée
     */
    public Optional<Organisation> findByNumeroEnregistrement(String numeroEnregistrement) {
        return find("numeroEnregistrement = ?1", numeroEnregistrement).firstResultOptional();
    }

    /**
     * Trouve toutes les organisations actives
     * 
     * @return liste des organisations actives
     */
    public List<Organisation> findAllActives() {
        return find("statut = 'ACTIVE' and actif = true").list();
    }

    /**
     * Trouve toutes les organisations actives avec pagination
     * 
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations actives
     */
    public List<Organisation> findAllActives(Page page, Sort sort) {
        return find("statut = 'ACTIVE' and actif = true", sort).page(page).list();
    }

    /**
     * Compte le nombre d'organisations actives
     * 
     * @return nombre d'organisations actives
     */
    public long countActives() {
        return count("statut = 'ACTIVE' and actif = true");
    }

    /**
     * Trouve les organisations par statut
     * 
     * @param statut le statut recherché
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations avec le statut spécifié
     */
    public List<Organisation> findByStatut(String statut, Page page, Sort sort) {
        return find("statut = ?1", sort, statut).page(page).list();
    }

    /**
     * Trouve les organisations par type
     * 
     * @param typeOrganisation le type d'organisation
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations du type spécifié
     */
    public List<Organisation> findByType(String typeOrganisation, Page page, Sort sort) {
        return find("typeOrganisation = ?1", sort, typeOrganisation).page(page).list();
    }

    /**
     * Trouve les organisations par ville
     * 
     * @param ville la ville
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations de la ville spécifiée
     */
    public List<Organisation> findByVille(String ville, Page page, Sort sort) {
        return find("ville = ?1", sort, ville).page(page).list();
    }

    /**
     * Trouve les organisations par pays
     * 
     * @param pays le pays
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations du pays spécifié
     */
    public List<Organisation> findByPays(String pays, Page page, Sort sort) {
        return find("pays = ?1", sort, pays).page(page).list();
    }

    /**
     * Trouve les organisations par région
     * 
     * @param region la région
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations de la région spécifiée
     */
    public List<Organisation> findByRegion(String region, Page page, Sort sort) {
        return find("region = ?1", sort, region).page(page).list();
    }

    /**
     * Trouve les organisations filles d'une organisation parente
     * 
     * @param organisationParenteId l'ID de l'organisation parente
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations filles
     */
    public List<Organisation> findByOrganisationParente(UUID organisationParenteId, Page page, Sort sort) {
        return find("organisationParenteId = ?1", sort, organisationParenteId).page(page).list();
    }

    /**
     * Trouve les organisations racines (sans parent)
     * 
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations racines
     */
    public List<Organisation> findOrganisationsRacines(Page page, Sort sort) {
        return find("organisationParenteId is null", sort).page(page).list();
    }

    /**
     * Recherche d'organisations par nom ou nom court
     * 
     * @param recherche terme de recherche
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations correspondantes
     */
    public List<Organisation> findByNomOrNomCourt(String recherche, Page page, Sort sort) {
        String pattern = "%" + recherche.toLowerCase() + "%";
        return find("lower(nom) like ?1 or lower(nomCourt) like ?1", sort, pattern).page(page).list();
    }

    /**
     * Recherche avancée d'organisations
     *
     * @param nom nom (optionnel)
     * @param typeOrganisation type (optionnel)
     * @param statut statut (optionnel)
     * @param ville ville (optionnel)
     * @param region région (optionnel)
     * @param pays pays (optionnel)
     * @param page pagination
     * @return liste filtrée des organisations
     */
    public List<Organisation> rechercheAvancee(String nom, String typeOrganisation, String statut,
                                             String ville, String region, String pays, Page page) {
        StringBuilder query = new StringBuilder("1=1");
        Map<String, Object> parameters = new HashMap<>();

        if (nom != null && !nom.isEmpty()) {
            query.append(" and (lower(nom) like :nom or lower(nomCourt) like :nom)");
            parameters.put("nom", "%" + nom.toLowerCase() + "%");
        }

        if (typeOrganisation != null && !typeOrganisation.isEmpty()) {
            query.append(" and typeOrganisation = :typeOrganisation");
            parameters.put("typeOrganisation", typeOrganisation);
        }

        if (statut != null && !statut.isEmpty()) {
            query.append(" and statut = :statut");
            parameters.put("statut", statut);
        }

        if (ville != null && !ville.isEmpty()) {
            query.append(" and lower(ville) like :ville");
            parameters.put("ville", "%" + ville.toLowerCase() + "%");
        }

        if (region != null && !region.isEmpty()) {
            query.append(" and lower(region) like :region");
            parameters.put("region", "%" + region.toLowerCase() + "%");
        }

        if (pays != null && !pays.isEmpty()) {
            query.append(" and lower(pays) like :pays");
            parameters.put("pays", "%" + pays.toLowerCase() + "%");
        }

        return find(query.toString(), Sort.by("nom").ascending(), parameters)
                .page(page).list();
    }

    /**
     * Compte les nouvelles organisations depuis une date donnée
     * 
     * @param depuis date de référence
     * @return nombre de nouvelles organisations
     */
    public long countNouvellesOrganisations(LocalDate depuis) {
        return count("dateCreation >= ?1", depuis.atStartOfDay());
    }

    /**
     * Trouve les organisations publiques (visibles dans l'annuaire)
     * 
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations publiques
     */
    public List<Organisation> findOrganisationsPubliques(Page page, Sort sort) {
        return find("organisationPublique = true and statut = 'ACTIVE' and actif = true", sort)
                .page(page).list();
    }

    /**
     * Trouve les organisations acceptant de nouveaux membres
     * 
     * @param page pagination
     * @param sort tri
     * @return liste paginée des organisations acceptant de nouveaux membres
     */
    public List<Organisation> findOrganisationsOuvertes(Page page, Sort sort) {
        return find("accepteNouveauxMembres = true and statut = 'ACTIVE' and actif = true", sort)
                .page(page).list();
    }

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

    /**
     * Compte les organisations par type
     * 
     * @param typeOrganisation le type d'organisation
     * @return nombre d'organisations de ce type
     */
    public long countByType(String typeOrganisation) {
        return count("typeOrganisation = ?1", typeOrganisation);
    }
}
