/*
 * This file is part of the Meeds project (https://meeds.io/).
 * Copyright (C) 2020 - 2022 Meeds Association contact@meeds.io
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package io.meeds.tenant.provisioning.service;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import javax.mail.internet.MimeMessage;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import org.exoplatform.commons.ObjectAlreadyExistsException;
import org.exoplatform.commons.utils.HTMLSanitizer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.component.RequestLifeCycle;
import org.exoplatform.services.mail.MailService;
import org.exoplatform.wiki.WikiException;
import org.exoplatform.wiki.model.Page;
import org.exoplatform.wiki.service.NoteService;

import io.meeds.deeds.constant.ObjectNotFoundException;
import io.meeds.deeds.listerner.model.EmailSendingCommand;
import io.meeds.tenant.provisioning.model.AutomaticStatus;
import io.meeds.tenant.provisioning.model.EmailTemplate;
import io.meeds.tenant.provisioning.storage.EmailTemplateRepository;

@Component
public class DeedEmailService {

  private static final Logger               LOG =
                                                LoggerFactory.getLogger(DeedEmailService.class);

  protected NoteService                     noteService;

  protected MailService                     mailService;

  @Autowired
  private TenantProvisioningSettingsService tenantProvisioningSettingsService;

  @Autowired
  private TenantProvisioningService         tenantProvisioningService;

  @Autowired
  private EmailTemplateRepository           emailTemplateRepository;

  public List<EmailTemplate> getEmailTemplates() {
    return emailTemplateRepository.findAll(Pageable.unpaged()).getContent();
  }

  public EmailTemplate createTemplate(EmailTemplate emailTemplate) throws ObjectAlreadyExistsException {
    if (emailTemplate.getId() != null) {
      throw new ObjectAlreadyExistsException("Email Template already exists with id " + emailTemplate.getId());
    }
    return emailTemplateRepository.save(emailTemplate);
  }

  public EmailTemplate updateTemplate(EmailTemplate emailTemplate) throws ObjectNotFoundException {
    String id = emailTemplate.getId();
    EmailTemplate storedTemplate = emailTemplateRepository.findById(id).orElse(null);
    if (storedTemplate == null) {
      throw new ObjectNotFoundException("Email Template with id " + id + " doesn't exists");
    }
    return emailTemplateRepository.save(emailTemplate);
  }

  public void deleteTemplate(String id) throws ObjectNotFoundException {
    EmailTemplate storedTemplate = emailTemplateRepository.findById(id).orElse(null);
    if (storedTemplate == null) {
      throw new ObjectNotFoundException("Email Template with id " + id + " doesn't exist");
    }
    emailTemplateRepository.deleteById(id);
  }

  public void sendEmail(EmailSendingCommand emailSendingCommand) throws Exception {
    AutomaticStatus status = AutomaticStatus.valueOf(emailSendingCommand.getTemplate());
    EmailTemplate emailTemplate = emailTemplateRepository.findByAutomaticStatus(status)
                                                         .orElseThrow(() -> new ObjectNotFoundException("Associated email template for automatic status "
                                                             + status + " not found"));
    Page notePage = getNotePage(emailTemplate);
    if (notePage == null) {
      throw new ObjectNotFoundException("Email Note Page with id " + emailTemplate.getNotePageId() + " wasn't found");
    }

    String subject = emailTemplate.getMailSubject();
    String content = notePage.getContent();
    if (!CollectionUtils.isEmpty(emailSendingCommand.getParameters())) {
      Set<Entry<String, String>> parameterEntries = emailSendingCommand.getParameters().entrySet();
      for (Entry<String, String> entry : parameterEntries) {
        content = content.replace("## " + entry.getKey() + " ##", entry.getValue());
        subject = subject.replace("## " + entry.getKey() + " ##", entry.getValue());
      }
    }
    sendEmail(emailSendingCommand.getEmail(), subject, content, true);
  }

  public void sendEmail(long nftId, AutomaticStatus status) throws Exception {
    EmailTemplate emailTemplate = emailTemplateRepository.findByAutomaticStatus(status)
                                                         .orElseThrow(() -> new ObjectNotFoundException("Associated email template for automatic status "
                                                             + status + " not found"));
    if (emailTemplate != null && emailTemplate.getNotePageId() > 0) {
      Page notePage = getNotePage(emailTemplate);
      if (notePage == null) {
        throw new ObjectNotFoundException("Email Note Page with id " + emailTemplate.getNotePageId() + " wasn't found");
      }

      String subject = emailTemplate.getMailSubject();
      String content = notePage.getContent();
      sendEmail(nftId, subject, content, true);
    }
  }

  public void sendEmail(long nftId, String emailSubject, String emailContent, boolean async) throws Exception {
    String to = tenantProvisioningService.getManagerEmail(nftId);
    sendEmail(to, emailSubject, emailContent, async);
  }

  public void sendEmail(String to, String emailSubject, String emailContent, boolean async) throws Exception {
    if (StringUtils.isBlank(to)) {
      LOG.warn("Can't send mail to empty email");
      return;
    }
    String from = tenantProvisioningSettingsService.getSenderFullEmail();
    String[] bcc = tenantProvisioningSettingsService.getEmailBcc();
    String signature = tenantProvisioningSettingsService.getEmailSignature();
    byte[] emailLogoBytes = tenantProvisioningSettingsService.getEmailLogoData();
    String emailLogoBase64 = emailLogoBytes == null || emailLogoBytes.length > 0 ? Base64.getEncoder()
                                                                                 .encodeToString(emailLogoBytes)
                                                                                 : "";
    
    MimeMessage mimeMessage = new MimeMessage(getMailService().getMailSession());
    MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8.name());
    messageHelper.setFrom(from);
    messageHelper.setTo(to);
    if (bcc != null && bcc.length > 0) {
      messageHelper.setBcc(bcc);
    }
    messageHelper.setSubject(StringEscapeUtils.unescapeHtml(HTMLSanitizer.sanitize(emailSubject)));
    String htmlContent = HTMLSanitizer.sanitize(emailContent + "<br/><p>" + signature + "</p>");
    String imgFooter = StringUtils.isBlank(emailLogoBase64) ? ""
                                                              : ("<br/><img src=\"data:" + MediaType.IMAGE_PNG_VALUE + ";base64,"
                                                                  + emailLogoBase64
                                                                  + "\" style=\"max-width: 200px; min-width: 100px;\">");
    messageHelper.setText(htmlContent + imgFooter, true);
    if (async) {
      getMailService().sendMessageInFuture(messageHelper.getMimeMessage());
    } else {
      getMailService().sendMessage(messageHelper.getMimeMessage());
    }
  }

  private Page getNotePage(EmailTemplate emailTemplate) throws WikiException {
    startKernelTransaction();
    try {
      return getNoteService().getNoteById(String.valueOf(emailTemplate.getNotePageId()));
    } finally {
      commitKernelTransaction();
    }
  }

  private MailService getMailService() {
    if (mailService == null) {
      mailService = getContainer().getComponentInstanceOfType(MailService.class);
    }
    return mailService;
  }

  private NoteService getNoteService() {
    if (noteService == null) {
      noteService = getContainer().getComponentInstanceOfType(NoteService.class);
    }
    return noteService;
  }

  private PortalContainer getContainer() {
    return PortalContainer.getInstance();
  }

  private void startKernelTransaction() {
    try {
      PortalContainer container = getContainer();
      ExoContainerContext.setCurrentContainer(container);
      RequestLifeCycle.begin(container);
    } catch (Throwable e) {// NOSONAR fails on Unit Tests for Kernel Integration
      LOG.warn("Error starting Kernel transaction", e);
    }
  }

  private void commitKernelTransaction() {
    try {
      RequestLifeCycle.end();
    } catch (Throwable e) {// NOSONAR fails on Unit Tests for Kernel Integration
      LOG.warn("Error committing Kernel transaction", e);
    }
  }

}
