package com.ffii.lioner.modules.common.mail.service;

import java.io.IOException;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;

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.lioner.modules.common.ErrorCodes;
import com.ffii.lioner.modules.common.LocalDateAdapter;
import com.ffii.lioner.modules.common.SettingNames;
import com.ffii.lioner.modules.common.mail.pojo.MailRequest;
import com.ffii.lioner.modules.settings.service.SettingsService;
import com.ffii.core.exception.InternalServerErrorException;
import com.ffii.core.support.JdbcDao;
import com.ffii.core.utils.LocaleUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

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

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

  public MailService(JdbcDao jdbcDao, MailSenderService mailSenderService, Configuration freemarkerConfig,
      SettingsService settingsService) {
    this.jdbcDao = jdbcDao;
    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 = null;
      if (mailRequest.getTemplate() != null) {
        try {
          template = freemarkerConfig
              .getTemplate(mailRequest.getTemplate() + "_" + LocaleUtils.toLocaleStr(locale) + ".ftl");
        } catch (TemplateNotFoundException e) {
          template = freemarkerConfig.getTemplate(mailRequest.getTemplate() + ".ftl");
        }
      } else {
        // Create a FreeMarker configuration
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_32);

        // Create a template loader and add the template content
        StringTemplateLoader templateLoader = new StringTemplateLoader();
        templateLoader.putTemplate("myTemplate", mailRequest.getTemplateContent());
        configuration.setTemplateLoader(templateLoader);

        try {
          // Get the template by name
          template = configuration.getTemplate("myTemplate");
        } catch (IOException e) {
          // Handle any IO exceptions
        }
      }

      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) throws ParseException {
    try {
      doSend(mailRequests, locale);
    } catch (MessagingException | IOException | TemplateException e) {
      throw new InternalServerErrorException(ErrorCodes.SEND_EMAIL_ERROR, e);
    }
  }

  public void sendARS(List<MailRequest> mailRequests, Locale locale, Long eventId, Long subDivisionId, Long userId,
    String reminderType) throws ParseException {
    // Create an instance of the GsonBuilder
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(LocalDate.class, new LocalDateAdapter());
    Gson gson = gsonBuilder.create();

    String jsonString = gson.toJson(mailRequests.get(0));
    try {
      doSend(mailRequests, locale);
    } catch (MessagingException | IOException | TemplateException e) {
      Map<String, Object> value = Map.of(
          "eventId", eventId,
          "subDivisionId", subDivisionId,
          "reminderType", reminderType,
          "userId", userId,
          "sendDate", LocalDateTime.now(),
          "success", false,
          "response", e.getMessage(),
          "content", jsonString,
          "resendSuccess", false);
      jdbcDao.executeUpdate(
          "INSERT IGNORE INTO todo_reminder_email_log (eventId, subDivisionId, reminderType, userId, sendDate, success, response, content, resendSuccess)"
              + " VALUE (:eventId, :subDivisionId, :reminderType, :userId, :sendDate, :success, :response, :content, :resendSuccess)",
          value);
    }
    Map<String, Object> value = Map.of(
        "eventId", eventId,
        "subDivisionId", subDivisionId,
        "reminderType", reminderType,
        "userId", userId,
        "sendDate", LocalDateTime.now(),
        "success", true,
        "content", jsonString,
        "resendSuccess", false);
    jdbcDao.executeUpdate(
        "INSERT IGNORE INTO todo_reminder_email_log (eventId, subDivisionId, reminderType, userId, sendDate, success, content, resendSuccess)"
            + " VALUE (:eventId, :subDivisionId, :reminderType, :userId, :sendDate, :success ,:content, :resendSuccess)",
        value);
  }

  public void resendARS(MailRequest mailRequestObj, Long emailLogId) throws ParseException {
    List<MailRequest> mailRequests = new ArrayList<MailRequest>();
    mailRequests.add(mailRequestObj);
    Locale locale = Locale.ENGLISH;

    // Convert the object to JSON string
    try {
      doSend(mailRequests, locale);
    } catch (MessagingException | IOException | TemplateException e) {
      //error still occur
    }
    jdbcDao.executeUpdate(
      " UPDATE todo_reminder_email_log "
      + " SET resendSuccess=1 "
      + " WHERE id = :emailLogId",
        Map.of("emailLogId", emailLogId));
  }

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

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

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

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

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

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

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

}

