import dataUriToBlob from "data-uri-to-blob";
import libmime from "libmime";
import union from "lodash/union";

import {
  fixNewLines,
  parseMIME,
  getHTML,
  getPlain,
  getAttachments,
  getAttachment,
  getGlobalHeaderValue
} from "../helpers/mailparser";
import { getCertificateChain } from "./signingUtilities";

const SIGNATURE_CONTENT_TYPE = "application/pkcs7-signature";
export const DEFAULT_ATTACHMENT_NAME = "attachment";

const splitParticipants = participantsList => {
  if (!participantsList) {
    return [];
  }

  const participants = participantsList.map(participants =>
    participants.split(",").map(p => p.trim())
  );
  return union.apply(null, participants);
};

export const parseSMIME = smimeString => {
  return new Promise(resolve => {
    setTimeout(async () => {
      const emailString = fixNewLines(smimeString);
      const parts = parseMIME(emailString);
      const html = getHTML(emailString, parts);
      const plain = getPlain(emailString, parts);
      const rawAttachments = getAttachments(emailString, parts);

      const attachments = [];
      let signatureBase64;
      for (const rawAttachment of rawAttachments) {
        const { contentType, base64 } = getAttachment(
          emailString,
          rawAttachment
        );

        if (contentType.indexOf(SIGNATURE_CONTENT_TYPE) !== -1) {
          signatureBase64 = base64;
        }

        const dataURI = "data:" + contentType + ";base64, " + base64;
        const blob = dataUriToBlob(dataURI);
        const filename = getFilenameFromHeaders(rawAttachment.headers);

        attachments.push({
          blob,
          filename
        });
      }

      const certificateChain = getCertificateChain(signatureBase64);

      const from = splitParticipants(getGlobalHeaderValue("from", parts));
      const to = splitParticipants(getGlobalHeaderValue("to", parts));
      const cc = splitParticipants(getGlobalHeaderValue("cc", parts));

      const message = {
        from,
        to,
        cc,
        subject: getGlobalHeaderValue("subject", parts).join(" "),
        html: extractHtmlBodyFromString(html),
        plain,
        attachments,
        certificateChain
      };

      resolve(message);
    }, 50);
  });
};

/**
 * Function extracts file name from content type header
 * @param headers
 * @returns {string} ('file.txt')
 */
export const getFilenameFromHeaders = headers => {
  const headersToSearch = ["content-type", "content-disposition"];

  const filename =
    headers &&
    Object.keys(headers)
      .filter(key => headersToSearch.includes(key))
      .reduce((result, key) => {
        const headerValue = libmime.parseHeaderValue(headers[key][0]);
        return result || headerValue.params.name || headerValue.params.filename;
      }, "");

  return filename || DEFAULT_ATTACHMENT_NAME;
};

/**
 * Function extracts all tags within <body></body> from provided string
 * and removes whitespaces between tags and HTML comments.
 * @param string
 * @returns {*}
 */
export const extractHtmlBodyFromString = string => {
  const extractBodyRegex = /<body.*?>([\s\S]+)<\/body>/gm;
  const bodyMatch = extractBodyRegex.exec(string);

  let body = string;

  if (bodyMatch && bodyMatch[1]) {
    body = bodyMatch[1];
  }

  return body
    .replace(/>\s+</gm, "><")
    .replace(/<!--[\s\S]*?-->/gm, "")
    .trim();
};