import nodemailer from "nodemailer";
import fs from 'fs/promises';
import _ from 'lodash';
import logging from "./logging.js";
import { promises } from "dns";

const log = logging.create("lib/email.js");

// TODO: move this out to an easy to access/change location
let configuration = {};

const configure_transporter = async () => {
  if(process.env.DANGER_ADMIN || process.env.DEBUG) {
    configuration = {
      streamTransport: true,
      debug: true,
      logger: log,
      newline: 'windows'
    }
  } else {
    configuration = JSON.parse(await fs.readFile("./secrets/email.json"));
    console.log("EMAIL CONFIG", configuration);
  }

 return nodemailer.createTransport(configuration);
}

// TODO: trash hacks to get the config out for the mail testing tool
export const get_config = () => configuration;

export const transporter = await configure_transporter();

export const load_templates = async (name) => {
  const html = await fs.readFile(`emails/${name}.html`);
  const txt = await fs.readFile(`emails/${name}.txt`);

  return {
    html: _.template(html),
    txt: _.template(txt)
  }
}

export const debug_templates = async (templates, name) => {
  if(process.env.DEBUG) {
    log.debug(`Writing debug email for ${name} to debug/emails/${name}.(html|txt)`);
    await fs.mkdir("debug/emails", { recursive: true});
    log.debug(`Emails written to debug/emails/${name}.(html|txt)`);
    await fs.writeFile(`debug/emails/${name}.html`, templates.html);
    await fs.writeFile(`debug/emails/${name}.txt`, templates.text);
  }
}

/* This never rejects the promise it makes but instead returns
 * any errors it receives or undefined if none.
 */
export const send_email = async (data) => {
  const result = new Promise((resolve, reject) => {
    transporter.sendMail(data, (err, info) => {
      try {
        if(err) {
          log.error(err);
          resolve(err);
        } else {
          if(process.env.DEBUG) {
            log.debug(info.envelope);
            log.debug(info.messageId);
            // I only do this when I'm lazy
            // info.message.pipe(process.stdout);
          }

          resolve(undefined);
        }
      } catch(error) {
        resolve(error);
      }
    });
  });

  return result;
}

const add_reverse_error = (result, error) => {
  if(result.reverse_errors === undefined) {
    result.reverse_errors = [];
  }

  let res_error = { error: {...error} };

  res_error.message = error.message;

  result.reverse_errors.push(res_error);
}

export const dns_check = async (hostname) => {
  let result = {
    ip4: {},
    ip6: {}
  };

  const res = new promises.Resolver();
  res.setServers(['8.8.8.8']);

  try {
    result.ip4.host = await res.resolve4(hostname);
  } catch(error) {
    // the errors in dns are stupid. You only get .message if you call it.
    result.ip4.error = {...error};
    result.ip4.error.message = error.message;
  }

  // no point doing reverse DNS if no IP4 address
  if(result.ip4.host) {
    result.ip4.reverse = [];

    for(let ip4 of result.ip4.host) {
      try {
        let host = await res.reverse(ip4);
        result.ip4.reverse.push(host);
      } catch(error) {
        add_reverse_error(res.ip4, error);
      }
    }
  }

  try {
    result.ip6.host = await res.resolve6(hostname);
  } catch(error) {
    result.ip6.error = {...error};
    result.ip6.error.message = error.message;
  }

  // no point in doing reverse DNS if no address
  if(result.ip6.host) {
    result.ip6.reverse = [];

    for(let ip6 of result.ip6.host) {
      try {
        let host = await res.reverse(ip6);
        result.ip6.reverse.push(host);
      } catch(error) {
        add_reverse_error(result.ip6, error);
      }
    }
  }

  try {
    result.mx = await res.resolveMx(hostname);
  } catch(error) {
    result.mx_error = {...error};
    result.mx_error.message = error.message;
  }

  try {
    result.spf = await res.resolveTxt(hostname);
  } catch(error) {
    result.spf_error = {...error};
    result.spf_error.message = error.message;
  }

  try {
    result.dmarc = await res.resolveTxt(`_dmarc.${hostname}`);
  } catch(error) {
    result.dmarc_error = {...error};
    result.dmarc_error.message = error.message;
  }

  return result;
}
