package com.ffii.core.common.mail.service;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import com.ffii.core.common.ErrorCodes;
import com.ffii.core.common.SettingNames;
import com.ffii.core.common.mail.pojo.MailRequest;
import com.ffii.core.exception.InternalServerErrorException;
import com.ffii.core.settings.service.SettingsService;
import com.ffii.core.utils.LocaleUtils;

import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;

@Service
public class MailService {
  protected final Log logger = LogFactory.getLog(getClass());

  private MailSenderService mailSenderService;
  private Configuration freemarkerConfig;
  private SettingsService settingsService;

  public MailService(MailSenderService mailSenderService, Configuration freemarkerConfig,
      SettingsService settingsService) {
    this.mailSenderService = mailSenderService;
    this.freemarkerConfig = freemarkerConfig;
    this.settingsService = settingsService;
  }

  private void doSend(List<MailRequest> mailRequests, Locale locale)
      throws MessagingException, TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException,
      TemplateException {
    JavaMailSender sender = mailSenderService.get();

    for (MailRequest mailRequest : mailRequests) {
      MimeMessage mimeMessage = sender.createMimeMessage();

      MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
      helper.setSubject(mailRequest.getSubject());

      Template template;
      try {
        template = freemarkerConfig
            .getTemplate(mailRequest.getTemplate() + "_" + LocaleUtils.toLocaleStr(locale) + ".ftl");
      } catch (TemplateNotFoundException e) {
        template = freemarkerConfig.getTemplate(mailRequest.getTemplate() + ".ftl");
      }

      helper.setText(
          FreeMarkerTemplateUtils.processTemplateIntoString(template, mailRequest.getArgs()),
          true);
      if (mailRequest.getFrom() != null) {
        helper.setFrom(mailRequest.getFrom());
      } else {
        helper.setFrom(settingsService.getString(SettingNames.MAIL_SMTP_USERNAME));
      }

      if (mailRequest.getPriority() != null)
        helper.setPriority(mailRequest.getPriority());
      if (mailRequest.getReplyTo() != null)
        helper.setReplyTo(mailRequest.getReplyTo());
      if (mailRequest.getTo() != null)
        helper.setTo(mailRequest.getTo().toArray(new InternetAddress[mailRequest.getTo().size()]));
      if (mailRequest.getCc() != null)
        helper.setCc(mailRequest.getCc().toArray(new InternetAddress[mailRequest.getCc().size()]));
      if (mailRequest.getBcc() != null)
        helper.setBcc(mailRequest.getBcc().toArray(new InternetAddress[mailRequest.getBcc().size()]));

      if (mailRequest.getAttachments() != null) {
        for (Map.Entry<String, byte[]> entry : mailRequest.getAttachments().entrySet()) {
          helper.addAttachment(entry.getKey(), new ByteArrayResource(entry.getValue()));
        }
      }
      sender.send(mimeMessage);
    }
  }

  public void send(List<MailRequest> mailRequests, Locale locale) {
    try {
      doSend(mailRequests, locale);
    } catch (MessagingException | IOException | TemplateException e) {
      throw new InternalServerErrorException(ErrorCodes.SEND_EMAIL_ERROR, e);
    }
  }

  @Async
  public void asyncSend(List<MailRequest> mailRequests, Locale locale) {
    try {
      doSend(mailRequests, locale);
    } catch (MessagingException | IOException | TemplateException e) {
      logger.error("send email error", e);
    }
  }

  public void send(List<MailRequest> mailRequests) {
    send(mailRequests, LocaleUtils.getLocale());
  }

  @Async
  public void asyncSend(List<MailRequest> mailRequests) {
    asyncSend(mailRequests, LocaleUtils.getLocale());
  }

  public void send(MailRequest mailRequest) {
    send(Arrays.asList(mailRequest));
  }

  @Async
  public void asyncSend(MailRequest mailRequest) {
    asyncSend(Arrays.asList(mailRequest));
  }

  public void send(MailRequest mailRequest, Locale locale) {
    send(Arrays.asList(mailRequest), locale);
  }

  @Async
  public void asyncSend(MailRequest mailRequest, Locale locale) {
    asyncSend(Arrays.asList(mailRequest), locale);
  }

}
