package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.TypeReference;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

/**
 * Repository pour les données de référence.
 *
 * <p>
 * Fournit les requêtes spécifiques aux
 * {@link TypeReference}, notamment le filtrage par
 * domaine, code et organisation.
 *
 * @author UnionFlow Team
 * @version 3.0
 * @since 2026-02-21
 */
@ApplicationScoped
public class TypeReferenceRepository
                extends BaseRepository<TypeReference> {

        /** Constructeur initialisant la classe d'entité. */
        public TypeReferenceRepository() {
                super(TypeReference.class);
        }

        /**
         * Liste les références actives d'un domaine,
         * triées par ordre d'affichage.
         *
         * <p>
         * Inclut les valeurs globales (organisation
         * {@code null}) et celles de l'organisation
         * spécifiée.
         *
         * @param domaine        le domaine fonctionnel
         * @param organisationId l'UUID de l'organisation
         *                       (peut être {@code null} pour global seul)
         * @return liste triée par ordre d'affichage
         */
        public List<TypeReference> findByDomaine(
                        String domaine, UUID organisationId) {
                String jpql = "SELECT t FROM TypeReference t"
                                + " WHERE t.domaine = :domaine"
                                + " AND t.actif = true"
                                + " AND (t.organisation IS NULL"
                                + " OR t.organisation.id = :orgId)"
                                + " ORDER BY t.ordreAffichage";
                return entityManager
                                .createQuery(jpql, TypeReference.class)
                                .setParameter("domaine", domaine.toUpperCase())
                                .setParameter("orgId", organisationId)
                                .getResultList();
        }

        /**
         * Recherche une référence par domaine et code.
         *
         * @param domaine le domaine fonctionnel
         * @param code    le code technique
         * @return la référence si trouvée
         */
        public Optional<TypeReference> findByDomaineAndCode(
                        String domaine, String code) {
                String jpql = "SELECT t FROM TypeReference t"
                                + " WHERE t.domaine = :domaine"
                                + " AND t.code = :code"
                                + " AND t.actif = true";
                return entityManager
                                .createQuery(jpql, TypeReference.class)
                                .setParameter("domaine", domaine.toUpperCase())
                                .setParameter("code", code.toUpperCase())
                                .getResultList().stream().findFirst();
        }

        /**
         * Retourne la valeur par défaut d'un domaine.
         *
         * @param domaine        le domaine fonctionnel
         * @param organisationId l'UUID de l'organisation
         *                       (peut être {@code null})
         * @return la valeur par défaut si définie
         */
        public Optional<TypeReference> findDefaut(
                        String domaine, UUID organisationId) {
                String jpql = "SELECT t FROM TypeReference t"
                                + " WHERE t.domaine = :domaine"
                                + " AND t.estDefaut = true"
                                + " AND t.actif = true"
                                + " AND (t.organisation IS NULL"
                                + " OR t.organisation.id = :orgId)";
                return entityManager
                                .createQuery(jpql, TypeReference.class)
                                .setParameter("domaine", domaine.toUpperCase())
                                .setParameter("orgId", organisationId)
                                .getResultList().stream().findFirst();
        }

        /**
         * Liste tous les domaines distincts existants.
         *
         * @return liste des noms de domaines uniques
         */
        public List<String> listDomaines() {
                String jpql = "SELECT DISTINCT t.domaine"
                                + " FROM TypeReference t"
                                + " ORDER BY t.domaine";
                return entityManager
                                .createQuery(jpql, String.class)
                                .getResultList();
        }

        /**
         * Vérifie l'unicité d'un code dans un domaine
         * et une organisation.
         *
         * @param domaine        le domaine fonctionnel
         * @param code           le code technique
         * @param organisationId l'UUID de l'organisation
         * @return {@code true} si le code existe déjà
         */
        public boolean existsByDomaineAndCode(
                        String domaine,
                        String code,
                        UUID organisationId) {
                String jpql = "SELECT COUNT(t)"
                                + " FROM TypeReference t"
                                + " WHERE t.domaine = :domaine"
                                + " AND t.code = :code"
                                + " AND (t.organisation IS NULL"
                                + " OR t.organisation.id = :orgId)";
                Long count = entityManager
                                .createQuery(jpql, Long.class)
                                .setParameter("domaine", domaine.toUpperCase())
                                .setParameter("code", code.toUpperCase())
                                .setParameter("orgId", organisationId)
                                .getSingleResult();
                return count > 0;
        }

        /**
         * Recherche le libellé d'une référence par domaine et code.
         *
         * @param domaine le domaine fonctionnel
         * @param code    le code technique
         * @return le libellé si trouvé, sinon le code
         */
        public String findLibelleByDomaineAndCode(String domaine, String code) {
                if (code == null)
                        return null;
                return findByDomaineAndCode(domaine, code)
                                .map(TypeReference::getLibelle)
                                .orElse(code);
        }

        /**
         * Recherche la severity d'une référence par domaine et code.
         *
         * @param domaine le domaine fonctionnel
         * @param code    le code technique
         * @return la severity si trouvée, sinon nulle
         */
        public String findSeverityByDomaineAndCode(String domaine, String code) {
                if (code == null)
                        return null;
                return findByDomaineAndCode(domaine, code)
                                .map(TypeReference::getSeverity)
                                .orElse(null);
        }
}
