package com.capgemini.framework.common;

import lombok.extern.slf4j.Slf4j;

import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.List;
import java.util.Properties;

/**
 * Utility class for sending email.
 **/
@Slf4j
public final class MailHelper {
  /**
   * Default email destinator for administration messages.
   **/
  public static final List<String> DEFAULT_DESTINATORS = SettingsHelper.getDefaultDestinatorEmail();
  private static final String MAIL_SMTP_PASSWORD = "mail.smtp.password";
  private static final String MAIL_SMTP_USER = "mail.smtp.user";
  private static final String MAIL_SMTP_AUTH = "mail.smtp.auth";
  private static final String MAIL_TRANSPORT_PROTOCOL = "mail.transport.protocol";
  private static final String MAIL_SMTP_SSL_TRUST = "mail.smtp.ssl.trust";
  private static final String MAIL_SMTP_PORT = "mail.smtp.port";
  private static final String MAIL_SMTP_HOST = "mail.smtp.host";
  private static final String MAIL_SMTP_ENABLE_STARTTLS = "mail.smtp.starttls.enable";
  private static final String FALSE = "false";
  private static final String TRUE = "true";
  // Standard connection settings
  private static final String DEFAULT_MAIL_USER = SettingsHelper.getDefaultExpeditorMailUsername();
  private static final String DEFAULT_MAIL_PASS = SettingsHelper.getDefaultExpeditorMailPassword();
  private static final String DEFAULT_SMTP_HOST = SettingsHelper.getDefaultSmtpHost();
  private static final String DEFAULT_SMTP_PORT = SettingsHelper.getDefaultSmtpPort();

  /**
   * Private constructor.
   **/
  private MailHelper() {
    // Hide constructor of utility classes
  }

  /**
   * Method sending email from the default sender.
   *
   * @param aDestinatorMail Email of the destinator
   * @param aSubject        Text content for the email subject
   * @param aContent        Text for the email content
   **/
  public static void send(final String aDestinatorMail, final String aSubject, final String aContent) {
    log.debug("Sending an email to user " + aDestinatorMail);

    // Check argument
    if (aDestinatorMail.isEmpty()) {
      log.warn("The mail destinator is empty, no message will be sent");
      return;
    }

    // Define JAVAX required properties
    Properties props = setupMailProperties();

    // For some specific servers, the authenticator should be set too
    Authenticator auth = new MailAuthenticator();

    // Set session
    Session theJavaxSession = Session.getInstance(props, auth);
    Transport transport = null;
    try {
      // Format email message
      MimeMessage message = new MimeMessage(theJavaxSession);
      message.setFrom(new InternetAddress(DEFAULT_MAIL_USER));
      message.addRecipient(Message.RecipientType.TO, new InternetAddress(aDestinatorMail));
      message.setSubject(aSubject);
      message.setContent(aContent, "text/html");
      message.saveChanges();

      // Set connection to SMTP server
      if (isAuthenticationMode()) {
        transport = theJavaxSession.getTransport("smtp");
        transport.connect(DEFAULT_SMTP_HOST, DEFAULT_MAIL_USER, DEFAULT_MAIL_PASS);
        transport.sendMessage(message, message.getAllRecipients());
      }
      else {
        Transport.send(message);
      }

      // When no error occurred at this point, the email has been sent properly
      log.debug("Mail sent to: {}", aDestinatorMail);
    }
    catch (AddressException e) {
      log.error("Unable to send email one of email adresses is invalid: " + DEFAULT_MAIL_USER + " or " + aDestinatorMail, e);
    }
    catch (MessagingException e) {
      log.error("Unable to send email", e);
    }
    finally {
      if (transport != null) {
        try {
          transport.close();
        }
        catch (MessagingException e) {
          log.error("Unable to close transport connection", e);
        }
      }
    }
  }

  /**
   * Method for batch-sending an email to several destinators.
   *
   * @param destinators List containing the Email of all the destinators
   * @param aSubject    Text content for the email subject
   * @param aContent    Text for the email content
   **/
  public static void sendAll(final List<String> destinators, final String aSubject, final String aContent) {
    for (String destinator : destinators) {
      MailHelper.send(destinator, aSubject, aContent);
    }
  }

  private static Properties setupMailProperties() {
    Properties props = new Properties();
    props.put(MAIL_SMTP_ENABLE_STARTTLS, TRUE);
    props.put(MAIL_SMTP_HOST, DEFAULT_SMTP_HOST);
    props.put(MAIL_SMTP_PORT, DEFAULT_SMTP_PORT);
    props.put(MAIL_SMTP_SSL_TRUST, DEFAULT_SMTP_HOST);
    props.put(MAIL_TRANSPORT_PROTOCOL, "smtp");

    if (isAuthenticationMode()) {
      props.put(MAIL_SMTP_AUTH, TRUE);
      props.put(MAIL_SMTP_USER, DEFAULT_MAIL_USER);
      props.put(MAIL_SMTP_PASSWORD, DEFAULT_MAIL_PASS);
    }
    else {
      props.put(MAIL_SMTP_AUTH, FALSE);
    }
    return props;
  }

  /**
   * Check a user and a password are defined for the SMTP connection.
   *
   * @return True when the user should be authenticated
   */
  private static boolean isAuthenticationMode() {
    return !DEFAULT_MAIL_USER.isEmpty() && !DEFAULT_MAIL_PASS.isEmpty();
  }

  private static final class MailAuthenticator extends Authenticator {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(DEFAULT_MAIL_USER, DEFAULT_MAIL_PASS);
    }
  }
}
