package eu.dnetlib.functionality.alert.alerter.smtp;

import java.net.URL;
import java.util.Properties;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;

import eu.dnetlib.domain.functionality.AlertSubscription;
import eu.dnetlib.functionality.alert.alerter.Alerter;
import eu.dnetlib.functionality.alert.alerter.AlerterException;

/**
 * This class implements an alerter that is capable of sending alerts via SMTP.
 * The subscribers served by this alerter must have a URI that is a valid email using the standard "mailto" scheme.
 * Emails are sent in HTML format.
 * The title provided is used as email subject and the message as email body.
 * If a link is provided it is used for an HTML anchor having the whole message as anchor text.
 * Finally a static footer is appended to the email body.
 * @author thanos@di.uoa.gr
 *
 */
public class SMTPAlerter extends Alerter {
	private static final Logger logger = Logger.getLogger(SMTPAlerter.class);
	private static final String SMTP = "SMTP";
	private static final String MAILTO = "mailto";
	private static final SortedSet<String> SUPPORTED_ALERT_MODES = new TreeSet<String>();
	private static final SortedSet<String> SUPPORTED_URI_SCHEMES = new TreeSet<String>();
	
	static {
		SUPPORTED_ALERT_MODES.add(SMTP);
		SUPPORTED_URI_SCHEMES.add(MAILTO);
	}
	
	private final Properties properties;
	private String username;
	private String password;
	private Session session;
	private String fromString;
	private String replyToString;
	private String emailFooter;
	private InternetAddress fromAddress;
	private InternetAddress replyToAddress;
	
	/**
	 * Construct a new SMTP alerter.
	 */
	public SMTPAlerter() {
		properties = new Properties();
		properties.setProperty("mail.transport.protocol", "smtp");
	}

	/**
	 * Set host.
	 * @param host the host to use 
	 */
	public void setHost(final String host) {
		properties.setProperty("mail.smtp.host", host);
	}
	
	/**
	 * Set port.
	 * @param port the port to use
	 */
	public void setPort(final int port) {
		properties.setProperty("mail.smtp.port", Integer.toString(port));
	}
	
	/**
	 * Set authentication.
	 * @param authentication if true use authentication; use no authentication otherwise
	 */
	public void setAuthentication(final boolean authentication) {
		properties.setProperty("mail.smtp.auth", Boolean.toString(authentication));
	}
	
	/**
	 * Set username.
	 * @param username the username to use
	 */
	public void setUsername(final String username) {
		this.username = username;
	}
	
	/**
	 * Set password.
	 * @param password the password to use
	 */
	public void setPassword(final String password) {
		this.password = password;
	}
	
	/**
	 * Set SSL. 
	 * @param ssl if true use SSL; use plain authentication otherwise
	 */
	public void setSsl(final boolean ssl) {
		properties.setProperty("mail.smtp.ssl.enable", Boolean.toString(ssl));
	}

	/**
	 * Set from.
	 * @param from the from SMTP header to use
	 */
	public void setFrom(final String from) {
		fromString = from;
	}
	
	/**
	 * Set reply to.
	 * @param replyTo the reply to SMTP header to use
	 */
	public void setReplyTo(final String replyTo) {
		replyToString = replyTo;
	}
	
	/**
	 * Set email footer.
	 * @param emailFooter the email footer to use
	 */
	public void setEmailFooter(final String emailFooter) {
		this.emailFooter = emailFooter;
	}

	@Override
	public void init() throws AlerterException {
		session = Session.getInstance(properties, new Authenticator() {
			@Override
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication(username, password);
			}
		});
		try {
			fromAddress = new InternetAddress(fromString);
			replyToAddress = new InternetAddress(replyToString);
			logger.info("SMTP alerter initialization complete");
		} catch (final MessagingException e) {
			throw new AlerterException("error initializing SMTP alerter", e);
		}
	}

	@Override
	public SortedSet<String> getSupportedAlertModes() {
		return SUPPORTED_ALERT_MODES;
	}

	@Override
	public void alert(final AlertSubscription subscription, final String title, final String message, final URL link) throws AlerterException {
		validateSubscription(subscription);
		try {
			final InternetAddress to = new InternetAddress(subscription.getSubscriber().getSchemeSpecificPart());
			final MimeMessage mail = new MimeMessage(session);
			mail.setFrom(fromAddress);
			mail.setSender(fromAddress);
			mail.setReplyTo(new Address[] {replyToAddress});
			mail.setRecipient(Message.RecipientType.TO, to);
			mail.setSubject(title);
			final StringBuilder body = new StringBuilder("<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\" /></head><body>");
			if (link != null)
				body.append("<p><a href=\"").append(StringEscapeUtils.escapeXml(link.toString())).append("\">");
			body.append(message);
			if (link != null)
				body.append("</a></p>");
			body.append(emailFooter);
			body.append("</body></html>");
			mail.setText(body.toString(), "UTF-8", "html");
			Transport.send(mail);
			logger.info("Sent mail to " + subscription.getSubscriber());
		} catch (final MessagingException e) {
			throw new AlerterException("error sending mail to " + subscription.getSubscriber(), e);
		}
	}
	
	@Override
	protected SortedSet<String> getSupportedUriSchemes() {
		return SUPPORTED_URI_SCHEMES;
	}
}
