package dev.lions.services;

import dev.lions.models.EmailMessage;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;

import lombok.extern.slf4j.Slf4j;

import dev.lions.config.ApplicationConfig;
import dev.lions.models.EmailTemplate;
import dev.lions.models.Notification;
import dev.lions.exceptions.EmailException;
import dev.lions.utils.TemplateProcessor;

import java.util.Map;
import java.util.Properties;

/**
 * Service gérant l'envoi des emails dans l'application.
 * Cette classe assure la configuration SMTP, le traitement des modèles
 * et l'envoi sécurisé des emails.
 */
@Slf4j
@ApplicationScoped
public class EmailService {

  @Inject
  ApplicationConfig config;

  @Inject
  TemplateProcessor templateProcessor;

  private static final int MAX_RETRY_ATTEMPTS = 3;
  private static final long RETRY_DELAY_MS = 1000;

  /**
   * Envoie un email basé sur un modèle.
   *
   * @param template Modèle d'email à utiliser
   */
  public void sendTemplatedEmail(@Valid @NotNull EmailTemplate template) {
    log.info("Préparation de l'envoi d'email avec le modèle : {}",
             template.getTemplateName());

    try {
      String htmlContent = processTemplate(template);

      EmailMessage message = EmailMessage.builder()
                                         .from(config.getSystemEmailAddress())
                                         .to(template.getRecipient())
                                         .subject(template.getSubject())
                                         .htmlContent(htmlContent)
                                         .build();

      sendEmailWithRetry(message);

    } catch (Exception e) {
      log.error("Erreur lors de l'envoi de l'email", e);
      throw new EmailException("Impossible d'envoyer l'email");
    }
  }

  /**
   * Traite le contenu du modèle avec les paramètres fournis.
   */
  private String processTemplate(EmailTemplate template) {
    log.debug("Traitement du modèle d'email : {}", template.getTemplateName());
    return templateProcessor.process(
        template.getContent(),
        template.getParameters()
    );
  }

  /**
   * Envoie un email avec mécanisme de reprise en cas d'échec.
   */
  private void sendEmailWithRetry(EmailMessage message) {
    Exception lastException = null;

    for (int attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
      try {
        sendEmail(message);
        log.info("Email envoyé avec succès à : {}", message.getTo());
        return;
      } catch (Exception e) {
        lastException = e;
        log.warn("Échec de l'envoi (tentative {}/{})",
                 attempt, MAX_RETRY_ATTEMPTS);

        if (attempt < MAX_RETRY_ATTEMPTS) {
          sleep(RETRY_DELAY_MS * attempt);
        }
      }
    }

    throw new EmailException(
        "Échec de l'envoi après " + MAX_RETRY_ATTEMPTS + " tentatives");
  }

  /**
   * Envoie effectif de l'email via SMTP.
   */
  private void sendEmail(EmailMessage message) throws MessagingException {
    Properties props = configureSmtpProperties();
    Session session = createSmtpSession(props);

    MimeMessage mimeMessage = new MimeMessage(session);
    configureMimeMessage(mimeMessage, message);

    Transport.send(mimeMessage);
  }

  /**
   * Configure les propriétés SMTP.
   */
  private Properties configureSmtpProperties() {
    Properties props = new Properties();
    props.put("mail.smtp.host", config.getSmtpHost());
    props.put("mail.smtp.port", config.getSmtpPort());
    props.put("mail.smtp.auth", "true");
    props.put("mail.smtp.starttls.enable", "true");
    props.put("mail.smtp.connectiontimeout", "5000");
    props.put("mail.smtp.timeout", "5000");

    if (config.isSmtpSslEnabled()) {
      props.put("mail.smtp.ssl.enable", "true");
      props.put("mail.smtp.ssl.trust", config.getSmtpHost());
    }

    return props;
  }

  /**
   * Crée une session SMTP authentifiée.
   */
  private Session createSmtpSession(Properties props) {
    return Session.getInstance(props, new jakarta.mail.Authenticator() {
      @Override
      protected jakarta.mail.PasswordAuthentication getPasswordAuthentication() {
        return new jakarta.mail.PasswordAuthentication(
            config.getSmtpUsername().orElseThrow(() ->
                                                     new EmailException("Nom d'utilisateur SMTP manquant")),
            config.getSmtpPassword().orElseThrow(() ->
                                                     new EmailException("Mot de passe SMTP manquant"))
        );
      }
    });
  }

  /**
   * Configure le message MIME avec les paramètres fournis.
   */
  private void configureMimeMessage(MimeMessage mimeMessage, EmailMessage message)
      throws MessagingException {
    mimeMessage.setFrom(new InternetAddress(message.getFrom()));
    mimeMessage.setRecipients(
        Message.RecipientType.TO,
        InternetAddress.parse(message.getTo())
    );
    mimeMessage.setSubject(message.getSubject());
    mimeMessage.setContent(message.getHtmlContent(), "text/html; charset=utf-8");
  }

  /**
   * Envoie une notification par email.
   */
  public void sendNotificationEmail(@NotNull Notification notification) {
    EmailTemplate template = EmailTemplate.builder()
                                          .templateName("notification-email")
                                          .recipient(config.getAdminEmailAddress())
                                          .subject("Notification système : " + notification.getTitle())
                                          .parameters(Map.of(
                                              "title", notification.getTitle(),
                                              "message", notification.getMessage(),
                                              "type", notification.getType().toString(),
                                              "timestamp", notification.getTimestamp().toString(),
                                              "actionUrl", notification.getActionUrl()
                                          ))
                                          .build();

    sendTemplatedEmail(template);
  }

  private void sleep(long milliseconds) {
    try {
      Thread.sleep(milliseconds);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new EmailException("Interruption pendant la reprise");
    }
  }
}

