/**
 * This file is part of Eclipse Steady.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
 */
package org.eclipse.steady.backend.util;

import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.validation.constraints.NotNull;

import org.apache.commons.configuration.Configuration;
import org.eclipse.steady.shared.util.StopWatch;
import org.eclipse.steady.shared.util.VulasConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>SmtpClient class.</p>
 */
public class SmtpClient {

  private static Logger log = LoggerFactory.getLogger(SmtpClient.class);

  /** Constant <code>SMTP_HOST="vulas.backend.smtp.host"</code> */
  public static final String SMTP_HOST = "vulas.backend.smtp.host";
  /** Constant <code>SMTP_PORT="vulas.backend.smtp.port"</code> */
  public static final String SMTP_PORT = "vulas.backend.smtp.port";
  /** Constant <code>SMTP_USER="vulas.backend.smtp.user"</code> */
  public static final String SMTP_USER = "vulas.backend.smtp.user";
  /** Constant <code>SMTP_PWD="vulas.backend.smtp.pwd"</code> */
  public static final String SMTP_PWD = "vulas.backend.smtp.pwd";

  private Properties props = null;

  /**
   * <p>Constructor for SmtpClient.</p>
   *
   * @throws java.lang.IllegalStateException if any.
   */
  public SmtpClient() throws IllegalStateException {
    this.props = SmtpClient.getSmtpProperties(VulasConfiguration.getGlobal().getConfiguration());
  }

  /**
   * <p>send.</p>
   *
   * @param _msg a {@link org.eclipse.steady.backend.util.Message} object.
   * @throws javax.mail.MessagingException if any.
   */
  public void send(@NotNull org.eclipse.steady.backend.util.Message _msg)
      throws MessagingException {
    if (_msg.hasAttachment()) this.sendWithAttachement(_msg);
    else log.error("Messages without attachment are not supported");
  }

  private void sendWithAttachement(@NotNull org.eclipse.steady.backend.util.Message _msg)
      throws MessagingException {
    final StopWatch sw = new StopWatch("Send message " + _msg).start();

    final Session session =
        Session.getInstance(
            props,
            new javax.mail.Authenticator() {
              protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(
                    VulasConfiguration.getGlobal().getConfiguration().getString(SMTP_USER),
                    VulasConfiguration.getGlobal().getConfiguration().getString(SMTP_PWD));
              }
            });

    try {
      final Message message = new MimeMessage(session);
      message.setSubject(_msg.getSubject());
      message.setFrom(new InternetAddress(_msg.getSender()));

      // Recipients
      final InternetAddress[] to = SmtpClient.transform(_msg.getRecipients());
      if (to.length == 0)
        throw new MessagingException("No valid recipients in " + _msg.getRecipients());
      else message.setRecipients(Message.RecipientType.TO, to);

      // Create a multipart message
      Multipart multipart = new MimeMultipart();

      // Text
      BodyPart messageBodyPart = new MimeBodyPart();
      messageBodyPart.setText(_msg.getBody());
      multipart.addBodyPart(messageBodyPart);

      // Attachment
      messageBodyPart = new MimeBodyPart();
      final DataSource source = new FileDataSource(_msg.getAttachment().toFile());
      messageBodyPart.setDataHandler(new DataHandler(source));
      messageBodyPart.setFileName(_msg.getAttachment().getFileName().toString());
      multipart.addBodyPart(messageBodyPart);

      // Send
      message.setContent(multipart);
      Transport.send(message);
      sw.stop();
    } catch (MessagingException e) {
      sw.stop(e);
      throw e;
    }
  }

  static final InternetAddress[] transform(Set<String> _recipients) {
    Set<InternetAddress> recipients = new HashSet<InternetAddress>();
    for (String to : _recipients) {
      try {
        recipients.add(InternetAddress.parse(to)[0]);
      } catch (AddressException e) {
        log.error("[" + to + "] is not a valid email address");
      }
    }
    return recipients.toArray(new InternetAddress[recipients.size()]);
  }

  /**
   * Returns {@link Properties} with all SMTP settings. Throws an {@link IllegalStateException} if
   * {@link #SMTP_HOST} or {@link SMTP_PORT} are not set.
   *
   * @param _cfg a {@link org.apache.commons.configuration.Configuration} object.
   * @return a {@link java.util.Properties} object.
   * @throws java.lang.IllegalStateException if any.
   */
  public static Properties getSmtpProperties(Configuration _cfg) throws IllegalStateException {
    final String host = _cfg.getString(SMTP_HOST);
    final String port = _cfg.getString(SMTP_PORT);
    if (host == null || port == null)
      throw new IllegalStateException(
          "Cannot setup SMTP client, configuration settings ["
              + SMTP_HOST
              + "] and/or ["
              + SMTP_PORT
              + "] are not present");

    final Properties props = new Properties();
    props.put("mail.smtp.auth", "true");
    // props.put("mail.smtp.starttls.enable", "true");
    props.put("mail.smtp.host", host);
    props.put("mail.smtp.port", port);

    return props;
  }
}
