package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.Membre;
import io.quarkus.arc.Unremovable;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.TypedQuery;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

/** Repository pour l'entité Membre avec UUID */
@ApplicationScoped
@Unremovable
public class MembreRepository extends BaseRepository<Membre> {


    public MembreRepository() {
        super(Membre.class);
    }

    /** Trouve un membre par son email */
    public Optional<Membre> findByEmail(String email) {
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT m FROM Membre m WHERE m.email = :email", Membre.class);
        query.setParameter("email", email);
        return query.getResultList().stream().findFirst();
    }

    /** Trouve un membre par son numéro */
    public Optional<Membre> findByNumeroMembre(String numeroMembre) {
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT m FROM Membre m WHERE m.numeroMembre = :numeroMembre", Membre.class);
        query.setParameter("numeroMembre", numeroMembre);
        return query.getResultList().stream().findFirst();
    }

    /** Trouve tous les membres actifs */
    public List<Membre> findAllActifs() {
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT m FROM Membre m WHERE m.actif = true", Membre.class);
        return query.getResultList();
    }

    /** Compte le nombre de membres actifs */
    public long countActifs() {
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(m) FROM Membre m WHERE m.actif = true", Long.class);
        return query.getSingleResult();
    }

    /** Trouve les membres par nom ou prénom (recherche partielle) */
    public List<Membre> findByNomOrPrenom(String recherche) {
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT m FROM Membre m WHERE LOWER(m.nom) LIKE LOWER(:recherche) OR LOWER(m.prenom) LIKE LOWER(:recherche)",
                Membre.class);
        query.setParameter("recherche", "%" + recherche + "%");
        return query.getResultList();
    }

    /** Trouve tous les membres actifs avec pagination et tri */
    public List<Membre> findAllActifs(Page page, Sort sort) {
        String orderBy = sort != null ? " ORDER BY " + buildOrderBy(sort) : "";
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT m FROM Membre m WHERE m.actif = true" + orderBy, Membre.class);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /** Trouve les membres par nom ou prénom avec pagination et tri */
    public List<Membre> findByNomOrPrenom(String recherche, Page page, Sort sort) {
        String orderBy = sort != null ? " ORDER BY " + buildOrderBy(sort) : "";
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT m FROM Membre m WHERE LOWER(m.nom) LIKE LOWER(:recherche) OR LOWER(m.prenom) LIKE LOWER(:recherche)"
                        + orderBy,
                Membre.class);
        query.setParameter("recherche", "%" + recherche + "%");
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /**
     * Trouve les membres appartenant à au moins une des organisations données (pour admin d'organisation).
     * Filtre les membres désactivés (actif=false) pour ne pas polluer les listes UI.
     */
    public List<Membre> findDistinctByOrganisationIdIn(Set<UUID> organisationIds, Page page, Sort sort) {
        if (organisationIds == null || organisationIds.isEmpty()) {
            return List.of();
        }
        String orderBy = sort != null ? " ORDER BY " + buildOrderBy(sort) : "";
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT DISTINCT m FROM Membre m JOIN m.membresOrganisations mo "
                        + "WHERE mo.organisation.id IN :organisationIds "
                        + "AND (m.actif = true OR m.actif IS NULL)"
                        + orderBy,
                Membre.class);
        query.setParameter("organisationIds", organisationIds);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /** Compte les membres distincts appartenant à au moins une des organisations données (filtre actif=true). */
    public long countDistinctByOrganisationIdIn(Set<UUID> organisationIds) {
        if (organisationIds == null || organisationIds.isEmpty()) {
            return 0L;
        }
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(DISTINCT m) FROM Membre m JOIN m.membresOrganisations mo "
                        + "WHERE mo.organisation.id IN :organisationIds "
                        + "AND (m.actif = true OR m.actif IS NULL)",
                Long.class);
        query.setParameter("organisationIds", organisationIds);
        return query.getSingleResult();
    }

    /** Compte les membres actifs distincts appartenant à au moins une des organisations données (pour dashboard par org). */
    public long countActifsDistinctByOrganisationIdIn(Set<UUID> organisationIds) {
        if (organisationIds == null || organisationIds.isEmpty()) {
            return 0L;
        }
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(DISTINCT m) FROM Membre m JOIN m.membresOrganisations mo WHERE mo.organisation.id IN :organisationIds AND (m.actif = true OR m.actif IS NULL)",
                Long.class);
        query.setParameter("organisationIds", organisationIds);
        return query.getSingleResult();
    }

    /**
     * Recherche par nom/prénom parmi les membres des organisations données (pour admin d'organisation).
     */
    public List<Membre> findByNomOrPrenomAndOrganisationIdIn(
            String recherche, Set<UUID> organisationIds, Page page, Sort sort) {
        if (organisationIds == null || organisationIds.isEmpty()) {
            return List.of();
        }
        String orderBy = sort != null ? " ORDER BY " + buildOrderBy(sort) : "";
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT DISTINCT m FROM Membre m JOIN m.membresOrganisations mo WHERE mo.organisation.id IN :organisationIds "
                        + "AND (LOWER(m.nom) LIKE LOWER(:recherche) OR LOWER(m.prenom) LIKE LOWER(:recherche))"
                        + orderBy,
                Membre.class);
        query.setParameter("organisationIds", organisationIds);
        query.setParameter("recherche", "%" + recherche + "%");
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /**
     * Compte les nouveaux membres depuis une date donnée (via MembreOrganisation)
     */
    public long countNouveauxMembres(LocalDate depuis) {
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(DISTINCT mo.membre) FROM MembreOrganisation mo WHERE mo.dateAdhesion >= :depuis",
                Long.class);
        query.setParameter("depuis", depuis);
        return query.getSingleResult();
    }

    /** Compte les nouveaux membres depuis une date pour une organisation (adhésions à cette org). */
    public long countNouveauxMembresByOrganisationId(LocalDate depuis, UUID organisationId) {
        if (organisationId == null) return 0L;
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(DISTINCT mo.membre) FROM MembreOrganisation mo WHERE mo.organisation.id = :organisationId AND mo.dateAdhesion >= :depuis",
                Long.class);
        query.setParameter("organisationId", organisationId);
        query.setParameter("depuis", depuis);
        return query.getSingleResult();
    }

    /** Compte les adhésions à une organisation dans une période (pour graphiques dashboard par org). */
    public long countNouveauxMembresByOrganisationIdInPeriod(LocalDate start, LocalDate end, UUID organisationId) {
        if (organisationId == null) return 0L;
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(DISTINCT mo.membre) FROM MembreOrganisation mo WHERE mo.organisation.id = :organisationId AND mo.dateAdhesion >= :start AND mo.dateAdhesion <= :end",
                Long.class);
        query.setParameter("organisationId", organisationId);
        query.setParameter("start", start);
        query.setParameter("end", end);
        return query.getSingleResult();
    }

    /** Trouve les membres par statut avec pagination */
    public List<Membre> findByStatut(boolean actif, Page page, Sort sort) {
        String orderBy = sort != null ? " ORDER BY " + buildOrderBy(sort) : "";
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT m FROM Membre m WHERE m.actif = :actif" + orderBy, Membre.class);
        query.setParameter("actif", actif);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /** Trouve un membre par son ID Keycloak (String → UUID) */
    public Optional<Membre> findByKeycloakUserId(String keycloakUserId) {
        if (keycloakUserId == null)
            return Optional.empty();
        try {
            UUID keycloakUUID = UUID.fromString(keycloakUserId);
            TypedQuery<Membre> q = entityManager.createQuery(
                    "SELECT m FROM Membre m WHERE m.keycloakId = :kid", Membre.class);
            q.setParameter("kid", keycloakUUID);
            return q.getResultList().stream().findFirst();
        } catch (IllegalArgumentException e) {
            return Optional.empty();
        }
    }

    /** Trouve les membres par tranche d'âge */
    public List<Membre> findByTrancheAge(int ageMin, int ageMax, Page page, Sort sort) {
        LocalDate dateNaissanceMax = LocalDate.now().minusYears(ageMin);
        LocalDate dateNaissanceMin = LocalDate.now().minusYears(ageMax + 1);
        String orderBy = sort != null ? " ORDER BY " + buildOrderBy(sort) : "";
        TypedQuery<Membre> query = entityManager.createQuery(
                "SELECT m FROM Membre m WHERE m.dateNaissance BETWEEN :dateMin AND :dateMax" + orderBy,
                Membre.class);
        query.setParameter("dateMin", dateNaissanceMin);
        query.setParameter("dateMax", dateNaissanceMax);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /** Recherche avancée de membres */
    public List<Membre> rechercheAvancee(
            String recherche,
            Boolean actif,
            LocalDate dateAdhesionMin,
            LocalDate dateAdhesionMax,
            Page page,
            Sort sort) {
        StringBuilder jpql = new StringBuilder("SELECT m FROM Membre m WHERE 1=1");

        if (recherche != null && !recherche.isEmpty()) {
            jpql.append(
                    " AND (LOWER(m.nom) LIKE LOWER(:recherche) OR LOWER(m.prenom) LIKE LOWER(:recherche) OR LOWER(m.email) LIKE LOWER(:recherche))");
        }
        if (actif != null) {
            jpql.append(" AND m.actif = :actif");
        }
        // dateAdhesion now in MembreOrganisation — ignorer pour cette recherche simple

        if (sort != null) {
            jpql.append(" ORDER BY ").append(buildOrderBy(sort));
        }

        TypedQuery<Membre> query = entityManager.createQuery(jpql.toString(), Membre.class);

        if (recherche != null && !recherche.isEmpty()) {
            query.setParameter("recherche", "%" + recherche + "%");
        }
        if (actif != null) {
            query.setParameter("actif", actif);
        }

        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /** Construit la clause ORDER BY à partir d'un Sort */
    private String buildOrderBy(Sort sort) {
        if (sort.getColumns().isEmpty()) {
            return "m.id";
        }
        StringBuilder orderBy = new StringBuilder();
        for (int i = 0; i < sort.getColumns().size(); i++) {
            if (i > 0) {
                orderBy.append(", ");
            }
            Sort.Column column = sort.getColumns().get(i);
            orderBy.append("m.").append(column.getName());
            if (column.getDirection() == Sort.Direction.Descending) {
                orderBy.append(" DESC");
            } else {
                orderBy.append(" ASC");
            }
        }
        return orderBy.toString();
    }

    /**
     * Compte les membres actifs dans une période et organisation
     *
     * @param organisationId UUID de l'organisation
     * @param debut          Date de début
     * @param fin            Date de fin
     * @return Nombre de membres actifs
     */
    public Long countMembresActifs(UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(DISTINCT mo.membre) FROM MembreOrganisation mo " +
                        "WHERE mo.organisation.id = :organisationId " +
                        "AND mo.membre.actif = true " +
                        "AND mo.dateAdhesion BETWEEN :debut AND :fin",
                Long.class);
        query.setParameter("organisationId", organisationId);
        query.setParameter("debut", debut.toLocalDate());
        query.setParameter("fin", fin.toLocalDate());
        return query.getSingleResult();
    }

    public Long countMembresInactifs(UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(DISTINCT mo.membre) FROM MembreOrganisation mo " +
                        "WHERE mo.organisation.id = :organisationId " +
                        "AND mo.membre.actif = false " +
                        "AND mo.dateAdhesion BETWEEN :debut AND :fin",
                Long.class);
        query.setParameter("organisationId", organisationId);
        query.setParameter("debut", debut.toLocalDate());
        query.setParameter("fin", fin.toLocalDate());
        return query.getSingleResult();
    }

    public Double calculerMoyenneAge(UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
        TypedQuery<Double> query = entityManager.createQuery(
                "SELECT AVG(YEAR(CURRENT_DATE) - YEAR(mo.membre.dateNaissance)) " +
                        "FROM MembreOrganisation mo " +
                        "WHERE mo.organisation.id = :organisationId " +
                        "AND mo.dateAdhesion BETWEEN :debut AND :fin",
                Double.class);
        query.setParameter("organisationId", organisationId);
        query.setParameter("debut", debut.toLocalDate());
        query.setParameter("fin", fin.toLocalDate());
        return query.getSingleResult();
    }
}
