package dev.lions.repositories;

import dev.lions.models.Contact;
import dev.lions.models.ContactStatus;
import dev.lions.exceptions.RepositoryException;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.TypedQuery;
import jakarta.validation.constraints.NotNull;

import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * Repository gérant la persistance des contacts dans l'application.
 * Cette classe assure le stockage, la récupération et la gestion des contacts
 * en implémentant des fonctionnalités spécifiques au traitement des demandes
 * de contact.
 */
@Slf4j
@ApplicationScoped
public class ContactRepository extends BaseRepository<Contact, Long> {

  @PersistenceContext
  private EntityManager entityManager;

  /**
   * Recherche les contacts par statut avec tri par date de soumission.
   * Cette méthode permet de filtrer les contacts selon leur état de traitement.
   *
   * @param status Statut des contacts à rechercher
   * @return Liste des contacts correspondant au statut
   */
  public List<Contact> findByStatus(@NotNull ContactStatus status) {
    log.debug("Recherche des contacts avec le statut : {}", status);

    try {
      TypedQuery<Contact> query = entityManager.createQuery(
          "SELECT c FROM Contact c " +
          "WHERE c.status = :status " +
          "ORDER BY c.submitDate DESC",
          Contact.class
      );

      query.setParameter("status", status);
      return query.getResultList();

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la recherche des contacts par statut", e);
    }
  }

  /**
   * Récupère les contacts non traités pour suivi.
   * Cette méthode retourne les contacts qui nécessitent une attention,
   * soit nouveaux soit en cours de traitement.
   *
   * @return Liste des contacts à traiter
   */
  public List<Contact> findUnprocessedContacts() {
    log.debug("Recherche des contacts non traités");

    try {
      TypedQuery<Contact> query = entityManager.createQuery(
          "SELECT c FROM Contact c " +
          "WHERE c.status IN (:statuses) " +
          "ORDER BY c.submitDate ASC",
          Contact.class
      );

      query.setParameter("statuses",
                         List.of(ContactStatus.NEW, ContactStatus.IN_PROGRESS));
      return query.getResultList();

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la recherche des contacts non traités", e);
    }
  }

  /**
   * Recherche les contacts soumis dans une période donnée.
   *
   * @param startDate Date de début de la période
   * @param endDate Date de fin de la période
   * @return Liste des contacts pour la période
   */
  public List<Contact> findBySubmitDateBetween(
      @NotNull LocalDateTime startDate,
      @NotNull LocalDateTime endDate) {

    log.debug("Recherche des contacts entre {} et {}", startDate, endDate);

    try {
      TypedQuery<Contact> query = entityManager.createQuery(
          "SELECT c FROM Contact c " +
          "WHERE c.submitDate BETWEEN :startDate AND :endDate " +
          "ORDER BY c.submitDate DESC",
          Contact.class
      );

      query.setParameter("startDate", startDate);
      query.setParameter("endDate", endDate);
      return query.getResultList();

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la recherche des contacts par période", e);
    }
  }

  /**
   * Met à jour le statut d'un contact.
   *
   * @param contactId Identifiant du contact
   * @param newStatus Nouveau statut
   * @param processDate Date de traitement
   * @return Contact mis à jour
   */
  public Optional<Contact> updateStatus(
      @NotNull Long contactId,
      @NotNull ContactStatus newStatus,
      LocalDateTime processDate) {

    log.debug("Mise à jour du statut du contact {} vers {}", contactId, newStatus);

    try {
      Contact contact = entityManager.find(Contact.class, contactId);
      if (contact == null) {
        return Optional.empty();
      }

      contact.setStatus(newStatus);
      if (processDate != null) {
        contact.setProcessDate(processDate);
      }

      Contact updatedContact = update(contact);
      log.info("Statut du contact {} mis à jour vers {}", contactId, newStatus);
      return Optional.of(updatedContact);

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de la mise à jour du statut du contact", e);
    }
  }

  /**
   * Ajoute une note interne à un contact.
   *
   * @param contactId Identifiant du contact
   * @param note Note à ajouter
   * @return Contact mis à jour
   */
  public Optional<Contact> addInternalNote(
      @NotNull Long contactId,
      @NotNull String note) {

    log.debug("Ajout d'une note au contact {}", contactId);

    try {
      Contact contact = entityManager.find(Contact.class, contactId);
      if (contact == null) {
        return Optional.empty();
      }

      String currentNotes = contact.getInternalNotes();
      String updatedNotes = currentNotes == null ? note :
                            currentNotes + "\n" + LocalDateTime.now() + ": " + note;

      contact.setInternalNotes(updatedNotes);
      Contact updatedContact = update(contact);

      log.info("Note ajoutée au contact {}", contactId);
      return Optional.of(updatedContact);

    } catch (Exception e) {
      throw new RepositoryException(
          "Erreur lors de l'ajout de la note au contact", e);
    }
  }
}