package dev.lions.services;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;

import lombok.extern.slf4j.Slf4j;

import dev.lions.models.Contact;
import dev.lions.models.ContactForm;
import dev.lions.models.ContactStatus;
import dev.lions.models.EmailTemplate;
import dev.lions.repositories.ContactRepository;
import dev.lions.events.ContactSubmissionEvent;
import dev.lions.exceptions.BusinessException;

import java.time.LocalDateTime;
import java.util.Map;

/**
 * Service gérant la logique métier des contacts.
 * Cette classe assure le traitement des demandes de contact, leur validation,
 * et la notification des parties concernées.
 */
@Slf4j
@ApplicationScoped
public class ContactService {

  @Inject
  private ContactRepository contactRepository;

  @Inject
  private EmailService emailService;

  @Inject
  private Event<ContactSubmissionEvent> contactEvent;

  /**
   * Traite un nouveau formulaire de contact.
   * Cette méthode valide les données, enregistre le contact et envoie
   * les notifications appropriées.
   *
   * @param form Formulaire de contact à traiter
   * @return Contact créé
   */
  @Transactional
  public Contact processContactForm(@Valid @NotNull ContactForm form) {
    log.info("Traitement d'une nouvelle demande de contact");

    try {
      validateContactForm(form);
      Contact contact = createContact(form);

      sendConfirmationEmails(contact);
      notifyContactSubmission(contact);

      log.info("Demande de contact traitée avec succès - ID: {}",
               contact.getId());
      return contact;

    } catch (BusinessException be) {
      log.warn("Erreur de validation du formulaire de contact", be);
      throw be;
    } catch (Exception e) {
      log.error("Erreur lors du traitement de la demande de contact", e);
      throw new BusinessException(
          "Impossible de traiter la demande de contact", e);
    }
  }

  /**
   * Valide les données du formulaire de contact.
   */
  private void validateContactForm(ContactForm form) {
    if (form.getName() == null || form.getName().trim().length() < 2) {
      throw new BusinessException("Le nom doit contenir au moins 2 caractères");
    }

    if (!isValidEmail(form.getEmail())) {
      throw new BusinessException("L'adresse email n'est pas valide");
    }

    if (form.getMessage() == null ||
        form.getMessage().trim().length() < 10 ||
        form.getMessage().length() > 1000) {
      throw new BusinessException(
          "Le message doit contenir entre 10 et 1000 caractères");
    }
  }

  /**
   * Crée une nouvelle entité Contact à partir du formulaire.
   */
  private Contact createContact(ContactForm form) {
    Contact contact = new Contact(
        form.getName(),
        form.getEmail(),
        form.getSubject(),
        form.getMessage()
    );

    contact.setStatus(ContactStatus.NEW);
    contact.setSubmitDate(LocalDateTime.now());

    return contactRepository.save(contact);
  }

  /**
   * Envoie les emails de confirmation.
   */
  private void sendConfirmationEmails(Contact contact) {
    sendCustomerConfirmation(contact);
    sendAdminNotification(contact);
  }

  /**
   * Envoie l'email de confirmation au client.
   */
  private void sendCustomerConfirmation(Contact contact) {
    EmailTemplate template = EmailTemplate.builder()
                                          .templateName("contact-confirmation")
                                          .recipient(contact.getEmail())
                                          .subject("Confirmation de votre message")
                                          .parameters(Map.of(
                                              "name", contact.getName(),
                                              "subject", contact.getSubject(),
                                              "message", contact.getMessage(),
                                              "contactId", contact.getId().toString()
                                          ))
                                          .build();

    emailService.sendTemplatedEmail(template);
  }

  /**
   * Envoie l'email de notification à l'administrateur.
   */
  private void sendAdminNotification(Contact contact) {
    EmailTemplate template = EmailTemplate.builder()
                                          .templateName("admin-contact-notification")
                                          .recipient(emailService.config.getAdminEmailAddress())
                                          .subject("Nouvelle demande de contact")
                                          .parameters(Map.of(
                                              "name", contact.getName(),
                                              "email", contact.getEmail(),
                                              "subject", contact.getSubject(),
                                              "message", contact.getMessage(),
                                              "timestamp", contact.getSubmitDate().toString(),
                                              "contactId", contact.getId().toString()
                                          ))
                                          .build();

    emailService.sendTemplatedEmail(template);
  }

  /**
   * Notifie le système de la soumission d'un nouveau contact.
   */
  private void notifyContactSubmission(Contact contact) {
    contactEvent.fire(new ContactSubmissionEvent(contact));
  }

  /**
   * Vérifie si une adresse email est valide.
   */
  private boolean isValidEmail(String email) {
    if (email == null || email.trim().isEmpty()) {
      return false;
    }
    String emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$";
    return email.matches(emailRegex);
  }

  /**
   * Met à jour le statut d'un contact.
   *
   * @param contactId Identifiant du contact
   * @param newStatus Nouveau statut
   * @param note Note optionnelle sur la mise à jour
   */
  @Transactional
  public void updateContactStatus(
      @NotNull Long contactId,
      @NotNull ContactStatus newStatus,
      String note) {

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

    try {
      Contact contact = contactRepository.findById(contactId)
                                         .orElseThrow(() -> new BusinessException("Contact non trouvé"));

      contact.setStatus(newStatus);
      contact.setProcessDate(LocalDateTime.now());

      if (note != null && !note.trim().isEmpty()) {
        addInternalNote(contact, note);
      }

      contactRepository.update(contact);

      log.info("Statut du contact mis à jour avec succès");

    } catch (Exception e) {
      log.error("Erreur lors de la mise à jour du statut du contact", e);
      throw new BusinessException(
          "Impossible de mettre à jour le statut du contact", e);
    }
  }

  /**
   * Ajoute une note interne à un contact.
   */
  private void addInternalNote(Contact contact, String note) {
    String currentNotes = contact.getInternalNotes();
    String timestamp = LocalDateTime.now().toString();
    String newNote = String.format("[%s] %s", timestamp, note);

    if (currentNotes == null || currentNotes.trim().isEmpty()) {
      contact.setInternalNotes(newNote);
    } else {
      contact.setInternalNotes(currentNotes + "\n" + newNote);
    }
  }
}