package com.atlassian.stash.internal.mail;

import com.atlassian.bitbucket.mail.MailHostConfiguration;
import com.atlassian.bitbucket.mail.MailProtocol;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.stereotype.Component;

import java.util.Locale;
import java.util.Properties;

/**
 * A default implementation of {@link JavaMailSenderFactory} which creates new {@code JavaMailSenderImpl} instances
 * configured according to the provided settings.
 */
@Component("javaMailSenderFactory")
public class DefaultJavaMailSenderFactory implements JavaMailSenderFactory {

    private static final String SMTP = "smtp";
    private static final String SMTPS = "smtps";
    private static final String MAIL_SMTP = "mail." + SMTP;
    private static final String MAIL_SMTPS = "mail." + SMTPS;
    //SMTP-only property to enable upgrading the plaintext connection to SSL/TLS using the STARTTLS extension
    private static final String SMTP_ENABLE_STARTTLS = "mail.smtp.starttls.enable";
    //SMTP-only property to require upgrading the plaintext connection to SSL/TLS using the STARTTLS extension and
    //fail if the server doesn't support it
    private static final String SMTP_REQUIRE_STARTTLS = "mail.smtp.starttls.required";

    private final String cryptoProtocols;
    private final String cryptoCiphers;

    @Autowired
    public DefaultJavaMailSenderFactory(@Value("${mail.crypto.protocols}") String cryptoProtocols, @Value("${mail.crypto.ciphers}") String cryptoCiphers) {
        this.cryptoProtocols = cryptoProtocols;
        this.cryptoCiphers = cryptoCiphers;
    }

    @Override
    public JavaMailSenderImpl create(MailHostConfiguration configuration, int connectTimeout, int sendTimeout) {
        if (configuration == null || StringUtils.isBlank(configuration.getHostname())) {
            return null;
        }

        String prefix = configuration.getProtocol() == MailProtocol.SMTP ? MAIL_SMTP : MAIL_SMTPS;

        JavaMailSenderImpl sender = new JavaMailSenderImpl();
        sender.setDefaultEncoding("UTF-8");
        sender.setHost(configuration.getHostname());
        if (configuration.getPort() != null) {
            sender.setPort(configuration.getPort());
        }

        Properties properties = new Properties();

        sender.setProtocol(configuration.getProtocol().name().toLowerCase(Locale.US));

        //For SMTP+STARTTLS and SMTPS
        if (configuration.getProtocol() == MailProtocol.SMTPS || configuration.isUseStartTls()) {
            //set specified protocols if any
            if (StringUtils.isNotBlank(cryptoProtocols)) {
                properties.put(sslProtocolsProp(prefix), cryptoProtocols);
            }
            //set specified ciphers if any
            if (StringUtils.isNotBlank(cryptoCiphers)) {
                properties.put(sslCiphersProp(prefix), cryptoCiphers);
            }
        }
        if (configuration.getProtocol() == MailProtocol.SMTP) {
            if (configuration.isUseStartTls()) {
                properties.setProperty(SMTP_ENABLE_STARTTLS, Boolean.TRUE.toString());
            }
            if (configuration.isRequireStartTls()) {
                properties.setProperty(SMTP_REQUIRE_STARTTLS, Boolean.TRUE.toString());
            }
        }
        if (connectTimeout > 0) {
            properties.setProperty(connectionTimeoutProp(prefix), String.valueOf(connectTimeout));
        }
        if (sendTimeout > 0) {
            properties.setProperty(sendTimeoutProp(prefix), String.valueOf(sendTimeout));
        }
        if (StringUtils.isNotBlank(configuration.getUsername())) {
            // if a username is provided, the mail session needs to be configured with the mail.smtp(s).auth property set
            // to true. See JavaMailSenderImpl javadoc
            properties.setProperty(authProp(prefix), Boolean.TRUE.toString());
            sender.setUsername(configuration.getUsername());

            if (StringUtils.isNotBlank(configuration.getPassword())) {
                sender.setPassword(configuration.getPassword());
            }
        }
        sender.setJavaMailProperties(properties);

        return sender;
    }

    private static String authProp(String prefix) {
        return prefix + ".auth";
    }

    private static String connectionTimeoutProp(String prefix) {
        return prefix + ".connectiontimeout";
    }

    private static String sendTimeoutProp(String prefix) {
        return prefix + ".timeout";
    }

    private static String sslProtocolsProp(String prefix) {
        return prefix + ".ssl.protocols";
    }

    private static String sslCiphersProp(String prefix) {
        return prefix + ".ssl.ciphers";
    }
}
