package dev.lions.repositories;

import dev.lions.models.EmailTemplate;
import dev.lions.exceptions.RepositoryException;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.TypedQuery;
import jakarta.transaction.Transactional;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Optional;

/**
 * Repository gérant la persistance des modèles d'emails de l'application.
 * Cette classe assure le stockage, la récupération et la gestion des templates
 * d'emails avec support multilingue et versionnement.
 */
@Slf4j
@ApplicationScoped
public class EmailTemplateRepository extends BaseRepository<EmailTemplate, Long> {

  @PersistenceContext
  private EntityManager entityManager;

  /**
   * Recherche un modèle d'email par son nom.
   * Cette méthode récupère la dernière version active du modèle.
   *
   * @param templateName Nom du modèle recherché
   * @return Modèle trouvé (Optional)
   */
  public Optional<EmailTemplate> findByName(@NotBlank String templateName) {
    log.debug("Recherche du modèle d'email : {}", templateName);

    try {
      TypedQuery<EmailTemplate> query = entityManager.createQuery(
          "SELECT t FROM EmailTemplate t " +
          "WHERE t.templateName = :name " +
          "AND t.active = true " +
          "ORDER BY t.version DESC",
          EmailTemplate.class
      );

      query.setParameter("name", templateName);
      query.setMaxResults(1);

      return Optional.of(query.getSingleResult());

    } catch (NoResultException e) {
      log.debug("Aucun modèle trouvé pour le nom : {}", templateName);
      return Optional.empty();

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la recherche du modèle d'email", e);
    }
  }

  /**
   * Recherche un modèle d'email par son nom et sa locale.
   * Permet de récupérer des modèles localisés spécifiques.
   *
   * @param templateName Nom du modèle
   * @param locale Code de la langue
   * @return Modèle trouvé (Optional)
   */
  public Optional<EmailTemplate> findByNameAndLocale(
      @NotBlank String templateName,
      @NotBlank String locale) {

    log.debug("Recherche du modèle d'email : {} pour la locale : {}",
              templateName, locale);

    try {
      TypedQuery<EmailTemplate> query = entityManager.createQuery(
          "SELECT t FROM EmailTemplate t " +
          "WHERE t.templateName = :name " +
          "AND t.locale = :locale " +
          "AND t.active = true " +
          "ORDER BY t.version DESC",
          EmailTemplate.class
      );

      query.setParameter("name", templateName);
      query.setParameter("locale", locale);
      query.setMaxResults(1);

      return Optional.of(query.getSingleResult());

    } catch (NoResultException e) {
      log.debug("Aucun modèle trouvé pour le nom : {} et la locale : {}",
                templateName, locale);
      return Optional.empty();

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la recherche du modèle d'email localisé", e);
    }
  }

  /**
   * Crée ou met à jour un modèle d'email.
   * Gère automatiquement le versionnement des modèles.
   *
   * @param template Modèle à sauvegarder
   * @return Modèle sauvegardé
   */
  @Transactional
  @Override
  public EmailTemplate save(EmailTemplate template) {
    log.debug("Sauvegarde du modèle d'email : {}", template.getTemplateName());

    try {
      if (template.getId() == null) {
        setNextVersion(template);
        entityManager.persist(template);
      } else {
        template = entityManager.merge(template);
      }

      entityManager.flush();
      log.info("Modèle d'email sauvegardé avec succès : {}",
               template.getTemplateName());

      return template;

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la sauvegarde du modèle d'email", e);
    }
  }

  /**
   * Définit la prochaine version pour un nouveau modèle.
   */
  private void setNextVersion(EmailTemplate template) {
    try {
      TypedQuery<Long> query = entityManager.createQuery(
          "SELECT MAX(t.version) FROM EmailTemplate t " +
          "WHERE t.templateName = :name",
          Long.class
      );

      query.setParameter("name", template.getTemplateName());
      Long maxVersion = query.getSingleResult();

      template.setVersion(maxVersion == null ? 1L : maxVersion + 1);

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la définition de la version du modèle", e);
    }
  }

  /**
   * Supprime tous les modèles d'un certain nom.
   *
   * @param templateName Nom des modèles à supprimer
   */
  @Transactional
  public void deleteByName(@NotBlank String templateName) {
    log.debug("Suppression des modèles d'email : {}", templateName);

    try {
      int deletedCount = entityManager.createQuery(
                                          "DELETE FROM EmailTemplate t WHERE t.templateName = :name")
                                      .setParameter("name", templateName)
                                      .executeUpdate();

      log.info("{} modèles d'email supprimés pour le nom : {}",
               deletedCount, templateName);

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la suppression des modèles d'email", e);
    }
  }

  /**
   * Vérifie l'existence d'un modèle par son nom.
   *
   * @param templateName Nom du modèle à vérifier
   * @return true si le modèle existe
   */
  public boolean existsByName(@NotBlank String templateName) {
    log.debug("Vérification de l'existence du modèle : {}", templateName);

    try {
      Long count = entityManager.createQuery(
                                    "SELECT COUNT(t) FROM EmailTemplate t WHERE t.templateName = :name",
                                    Long.class)
                                .setParameter("name", templateName)
                                .getSingleResult();

      return count > 0;

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la vérification de l'existence du modèle", e);
    }
  }
}