import aws = require('aws-sdk');
import formData = require('form-data');
import jsdom = require('jsdom');
import url = require('url');

const s3 = new aws.S3();
//Add your account below
const csiAccounts = [
  '857171411193', // twilight
  '157787312516', // twilight-test
  '001591884629', // twilight-shadow
  '523910602058', // cpe-tools-shadow
  '994136867826', // cpe-tools
  '182878055170', // csi-syslog
  '708209600803',  // orko
  '913752568110', // twitch-ce-benchmark
  '752483312237', // cpe-spotlight
  '170411929043', // mweb-test
  '709603242682', // mweb-staging
  '015957721237', // mweb-prod
];

const mwebAccounts = [
  '430230848193', // production
  '931523683592', // staging
]

const spotlightAccounts = [
  '632911754381', // spotlight-internal
]

const defaultAccounts = csiAccounts.concat(mwebAccounts, spotlightAccounts);

const accountList = process.env.TRUSTED_ACCOUNTS && process.env.TRUSTED_ACCOUNTS.split(',').map(id => id.replace(/-/g, '')) || defaultAccounts;

const approvalURLRegex = new RegExp(/(https:\/\/[a-z0-9-]+\.(acm-certificates|certificates)\.amazon\.com\/approvals\?[a-z0-9-=&]+)/i);
const domainRegex = new RegExp(/^Domain: ([a-z0-9-.]+\.[a-z0-9-]+)$/im);
const accountIdRegex = new RegExp(/^AWS account ID: ([0-9-]+)$/im);
const regionRegex = new RegExp(/^AWS Region name: ([a-z0-9-]+)$/im);
const certificateIdRegex = new RegExp(/^Certificate identifier: ([a-z0-9-]+)$/im);
const expirationDateRegex = new RegExp(/^Expiration date: (.+)$/mi);

export default async function processRecords(event: any, context: any, callback: any) {
  event.Records.map((record:any) => processRecord(record));
  callback(null, "ok");
}

function processRecord(record: any) {
  let bucket: string = record.s3.bucket.name;
  let key: string = record.s3.object.key;

  console.debug(`Bucket: ${bucket} Key: ${key}`);

  s3.getObject({Bucket: bucket, Key: key}, function(err: aws.AWSError, data: aws.S3.GetObjectOutput) {
    if (err) {
      console.error(err, err.stack);
      return;
    }

    let content = data.Body && data.Body.toString();
    if (!content) {
      return;
    }

    processContent(content);
  });
}

function processContent(content: string) {
  const approvalURL = getMatch(content, approvalURLRegex);
  if (!approvalURL) {
    console.log('No approval URL detected.  Nothing to do here.');
    return;
  }

  const domain = getMatch(content, domainRegex);
  const accountId = getMatch(content, accountIdRegex);
  const region = getMatch(content, regionRegex);
  const certificateId = getMatch(content, certificateIdRegex);
  const expirationDate = getMatch(content, expirationDateRegex) || 'unknown' ; 
  
  console.info(`ACM Validation Request Received!\n    Domain: ${domain}\n    AccountID: ${accountId}\n    Region: ${region}\n    CertificateId: ${certificateId}\n    Expiration Date: ${expirationDate}`);

  if (!domain || !accountId || !region || !certificateId) {
    console.error('Incomplete information gathered from the email. Aborting!');
    return;
  }

  if (!accountList.includes(accountId.replace(/-/g, ''))) {
    console.error('This request is from an un-trusted accountID.  Aborting!');
    return;
  }

  approveCertificate(approvalURL, accountId, certificateId);
}

async function approveCertificate(approvalURL: string, accountId: string, certificateId: string) {
  const dom : jsdom.JSDOM = await jsdom.JSDOM.fromURL(approvalURL);
  const document = dom.window.document;

  const pageData: { [key: string]: string } = {};

  // Parse certificate information.
  const tableCells = document.querySelectorAll<HTMLTableCellElement>('td.left-column, td.right-column');

  let key: string = '';
  for (let cell of tableCells) {
    let text = (cell.textContent || '').trim();    

    if (key) {
      pageData[key] = text;
      key = '';
    } else {
      key = text.toLowerCase();
    }
  }

  const pageDomain = pageData['domain name'];
  const pageAccountId = pageData['aws account number'];
  const pageRegion = pageData['aws region'];
  const pageCertificateId = pageData['certificate identifier'];

  console.info(`ACM Validation Page Parsed!\n    Domain: ${pageDomain}\n    AccountID: ${pageAccountId}\n    Region: ${pageRegion}\n    CertificateId: ${pageCertificateId}`);

  if (accountId != pageAccountId || certificateId != pageCertificateId) {
    console.error("Mismatch between emailed information and data on the page. Aborting! ");
    return;
  }

  // Parse security tokens.
  // Sadly JSDOM doesn't implement form.submit, so I can't just click the button.
  const formInputs = document.querySelectorAll<HTMLInputElement>('form[action="/approvals"] input:not([type=submit])');

  if (!formInputs || formInputs.length == 0) {
    console.error("Unable to gather form input data. Aborting!");
    return;
  }

  const form = new formData();
  for (let input of formInputs) {
    form.append(input.name, input.value);
  }

  const parsedURL = url.parse(approvalURL);
  const submitURL = `https://${parsedURL.host}${parsedURL.pathname}`

  form.submit(submitURL, function(error, response) {
    if (error) {
      console.error(error, error.stack);
      return;
    }

    if (response.statusCode != 200) {
      console.error(`Received a non ${response.statusCode} status code from the server.`)
      return;
    }

    console.info('Submit button has been "clicked"!')
  });
}

function getMatch(content: string, regex: RegExp) {
  let matches = regex.exec(content);
  return matches && matches[1] || '';
}
