package eu.dnetlib.lbs.events.output;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
import java.util.Properties;
import java.util.stream.Collectors;

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

import org.antlr.stringtemplate.StringTemplate;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import com.google.common.base.Splitter;

import eu.dnetlib.lbs.elasticsearch.Event;
import eu.dnetlib.lbs.properties.EmailProperties;
import eu.dnetlib.lbs.subscriptions.NotificationMode;
import eu.dnetlib.lbs.subscriptions.Subscription;

@Component
public class EmailDispatcher extends AbstractNotificationDispatcher<Message> {

	@Autowired
	private EmailProperties props;

	@Value("${lbs.mail.message.template}")
	private Resource emailTemplate;

	private static final int MAX_NUMEBER_OF_EVENTS = 20;

	private static final Log log = LogFactory.getLog(EmailDispatcher.class);

	@Override
	protected Message prepareAction(final Subscription subscription, final Event... events) throws Exception {

		if (subscription == null || StringUtils.isBlank(subscription.getSubscriber())) {
			log.warn("Invalid subscription");
			throw new IllegalArgumentException("Invalid subscription");
		}
		if (events.length == 0) {
			log.warn("Event list is empty");
			throw new IllegalArgumentException("Event list is empty");
		}

		final String topics = Arrays.stream(events).map(e -> e.getTopic()).distinct().collect(Collectors.joining(", "));

		final Session session = Session.getInstance(obtainProperties(), obtainAuthenticator());

		final MimeMessage mimeMessage = new MimeMessage(session);
		mimeMessage.setFrom(new InternetAddress(props.getFrom(), props.getFromName()));
		mimeMessage.setSubject("Notification for topic(s): " + topics);
		mimeMessage.setContent(generateMailContent(subscription, events), "text/html; charset=utf-8");
		mimeMessage.setSentDate(new Date());

		mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(subscription.getSubscriber()));

		if (StringUtils.isNotBlank(props.getCc())) {
			for (final String aCC : Splitter.on(",").omitEmptyStrings().trimResults().split(props.getCc())) {
				mimeMessage.addRecipient(Message.RecipientType.CC, new InternetAddress(aCC));
			}
		}

		log.info("Created mail, to: " + subscription.getSubscriber());

		return mimeMessage;
	}

	private String generateMailContent(final Subscription subscription, final Event... events) throws IOException {
		final StringTemplate st = new StringTemplate(IOUtils.toString(emailTemplate.getInputStream(), StandardCharsets.UTF_8));
		st.setAttribute("sub", subscription);

		st.setAttribute("total", events.length);
		st.setAttribute("max", MAX_NUMEBER_OF_EVENTS);

		if (events.length > MAX_NUMEBER_OF_EVENTS) {
			st.setAttribute("events", Arrays.copyOfRange(events, 0, MAX_NUMEBER_OF_EVENTS));
		} else {
			st.setAttribute("events", events);
		}
		return st.toString();
	}

	@Override
	protected void performAction(final Message message) throws Exception {
		log.info("Sending mail to " + Arrays.toString(message.getAllRecipients()) + "...");
		Transport.send(message);
		log.info("...sent");
	}

	private Properties obtainProperties() {
		final Properties p = new Properties();
		p.put("mail.transport.protocol", "smtp");
		p.put("mail.smtp.host", props.getSmtpHost());
		p.put("mail.smtp.port", props.getSmtpPort());
		p.put("mail.smtp.auth", Boolean.toString(StringUtils.isNotBlank(props.getSmtpUser())));
		return p;
	}

	private Authenticator obtainAuthenticator() {
		if (StringUtils.isBlank(props.getSmtpUser())) { return null; }

		return new Authenticator() {

			private final PasswordAuthentication authentication =
					new PasswordAuthentication(props.getSmtpUser(), props.getSmtpPassword());

			@Override
			protected PasswordAuthentication getPasswordAuthentication() {
				return authentication;
			}

		};
	}

	@Override
	public NotificationMode getMode() {
		return NotificationMode.EMAIL;
	}

	protected EmailProperties getProps() {
		return props;
	}

	protected void setProps(final EmailProperties props) {
		this.props = props;
	}

	protected Resource getEmailTemplate() {
		return emailTemplate;
	}

	protected void setEmailTemplate(final Resource emailTemplate) {
		this.emailTemplate = emailTemplate;
	}

}
