#!/usr/bin/env node
const { promises: fs } = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const glob = require('fast-glob');
const VERSION = require('../package.json').version;
const { getHighlights } = require('./highlights');

start().catch(console.error);

function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1);
}

async function start() {
  const { GITHUB_COMMENT_TOKEN, CIRCLE_PULL_REQUEST } = process.env;
  console.log('CIRCLE_PULL_REQUEST', CIRCLE_PULL_REQUEST);
  const { CIRCLE_SHA1 } = process.env;
  console.log('CIRCLE_SHA1', CIRCLE_SHA1);
  const { CIRCLE_BUILD_NUM } = process.env;
  console.log('CIRCLE_BUILD_NUM', CIRCLE_BUILD_NUM);
  const { CIRCLE_WORKFLOW_JOB_ID } = process.env;
  console.log('CIRCLE_WORKFLOW_JOB_ID', CIRCLE_WORKFLOW_JOB_ID);

  if (!CIRCLE_PULL_REQUEST) {
    console.warn(`No pull request detected for commit "${CIRCLE_SHA1}"`);
    return;
  }

  const CIRCLE_PR_NUMBER = CIRCLE_PULL_REQUEST.split('/').pop();
  const SHORT_SHA1 = CIRCLE_SHA1.slice(0, 7);
  const BUILD_LINK_BASE = `https://output.circle-artifacts.com/output/job/${CIRCLE_WORKFLOW_JOB_ID}/artifacts/0`;

  // build the github comment content

  // links to extension builds
  const platforms = ['chrome', 'firefox', 'opera'];
  const buildLinks = platforms
    .map((platform) => {
      const url = `${BUILD_LINK_BASE}/builds/metamask-${platform}-${VERSION}.zip`;
      return `<a href="${url}">${platform}</a>`;
    })
    .join(', ');
  const betaBuildLinks = platforms
    .map((platform) => {
      const url = `${BUILD_LINK_BASE}/builds-beta/metamask-beta-${platform}-${VERSION}.zip`;
      return `<a href="${url}">${platform}</a>`;
    })
    .join(', ');
  const flaskBuildLinks = platforms
    .map((platform) => {
      const url = `${BUILD_LINK_BASE}/builds-flask/metamask-flask-${platform}-${VERSION}.zip`;
      return `<a href="${url}">${platform}</a>`;
    })
    .join(', ');

  // links to bundle browser builds
  const bundles = {};
  const fileType = '.html';
  const sourceMapRoot = '/build-artifacts/source-map-explorer/';
  const bundleFiles = await glob(`.${sourceMapRoot}*${fileType}`);

  bundleFiles.forEach((bundleFile) => {
    const fileName = bundleFile.split(sourceMapRoot)[1];
    const bundleName = fileName.split(fileType)[0];
    const url = `${BUILD_LINK_BASE}${sourceMapRoot}${fileName}`;
    let fileRoot = bundleName;
    let fileIndex = bundleName.match(/-[0-9]{1,}$/u)?.index;

    if (fileIndex) {
      fileRoot = bundleName.slice(0, fileIndex);
      fileIndex = bundleName.slice(fileIndex + 1, bundleName.length);
    }

    const link = `<a href="${url}">${fileIndex || fileRoot}</a>`;

    if (fileRoot in bundles) {
      bundles[fileRoot].push(link);
    } else {
      bundles[fileRoot] = [link];
    }
  });

  const bundleMarkup = `<ul>${Object.keys(bundles)
    .map((key) => `<li>${key}: ${bundles[key].join(', ')}</li>`)
    .join('')}</ul>`;

  const coverageUrl = `${BUILD_LINK_BASE}/coverage/index.html`;
  const coverageLink = `<a href="${coverageUrl}">Report</a>`;

  const storybookUrl = `${BUILD_LINK_BASE}/storybook/index.html`;
  const storybookLink = `<a href="${storybookUrl}">Storybook</a>`;

  // links to bundle browser builds
  const depVizUrl = `${BUILD_LINK_BASE}/build-artifacts/build-viz/index.html`;
  const depVizLink = `<a href="${depVizUrl}">Build System</a>`;

  // link to artifacts
  const allArtifactsUrl = `https://circleci.com/gh/MetaMask/metamask-extension/${CIRCLE_BUILD_NUM}#artifacts/containers/0`;

  const contentRows = [
    `builds: ${buildLinks}`,
    `builds (beta): ${betaBuildLinks}`,
    `builds (flask): ${flaskBuildLinks}`,
    `build viz: ${depVizLink}`,
    `code coverage: ${coverageLink}`,
    `storybook: ${storybookLink}`,
    `<a href="${allArtifactsUrl}">all artifacts</a>`,
    `<details>
       <summary>bundle viz:</summary>
       ${bundleMarkup}
     </details>`,
  ];
  const hiddenContent = `<ul>${contentRows
    .map((row) => `<li>${row}</li>`)
    .join('\n')}</ul>`;
  const exposedContent = `Builds ready [${SHORT_SHA1}]`;
  const artifactsBody = `<details><summary>${exposedContent}</summary>${hiddenContent}</details>\n\n`;

  const benchmarkResults = {};
  for (const platform of platforms) {
    const benchmarkPath = path.resolve(
      __dirname,
      '..',
      path.join('test-artifacts', platform, 'benchmark', 'pageload.json'),
    );
    try {
      const data = await fs.readFile(benchmarkPath, 'utf8');
      const benchmark = JSON.parse(data);
      benchmarkResults[platform] = benchmark;
    } catch (error) {
      if (error.code === 'ENOENT') {
        console.log(`No benchmark data found for ${platform}; skipping`);
      } else {
        console.error(
          `Error encountered processing benchmark data for '${platform}': '${error}'`,
        );
      }
    }
  }

  const summaryPlatform = 'chrome';
  const summaryPage = 'home';
  let commentBody = artifactsBody;
  if (benchmarkResults[summaryPlatform]) {
    try {
      const summaryPageLoad = Math.round(
        parseFloat(benchmarkResults[summaryPlatform][summaryPage].average.load),
      );
      const summaryPageLoadMarginOfError = Math.round(
        parseFloat(
          benchmarkResults[summaryPlatform][summaryPage].marginOfError.load,
        ),
      );
      const benchmarkSummary = `Page Load Metrics (${summaryPageLoad} ± ${summaryPageLoadMarginOfError} ms)`;

      const allPlatforms = new Set();
      const allPages = new Set();
      const allMetrics = new Set();
      const allMeasures = new Set();
      for (const platform of Object.keys(benchmarkResults)) {
        allPlatforms.add(platform);
        const platformBenchmark = benchmarkResults[platform];
        const pages = Object.keys(platformBenchmark);
        for (const page of pages) {
          allPages.add(page);
          const pageBenchmark = platformBenchmark[page];
          const measures = Object.keys(pageBenchmark);
          for (const measure of measures) {
            allMeasures.add(measure);
            const measureBenchmark = pageBenchmark[measure];
            const metrics = Object.keys(measureBenchmark);
            for (const metric of metrics) {
              allMetrics.add(metric);
            }
          }
        }
      }

      const tableRows = [];
      for (const platform of allPlatforms) {
        const pageRows = [];
        for (const page of allPages) {
          const metricRows = [];
          for (const metric of allMetrics) {
            let metricData = `<td>${metric}</td>`;
            for (const measure of allMeasures) {
              metricData += `<td align="right">${Math.round(
                parseFloat(benchmarkResults[platform][page][measure][metric]),
              )}</td>`;
            }
            metricRows.push(metricData);
          }
          metricRows[0] = `<td rowspan="${
            allMetrics.size
          }">${capitalizeFirstLetter(page)}</td>${metricRows[0]}`;
          pageRows.push(...metricRows);
        }
        pageRows[0] = `<td rowspan="${
          allPages.size * allMetrics.size
        }">${capitalizeFirstLetter(platform)}</td>${pageRows[0]}`;
        for (const row of pageRows) {
          tableRows.push(`<tr>${row}</tr>`);
        }
      }

      const benchmarkTableHeaders = ['Platform', 'Page', 'Metric'];
      for (const measure of allMeasures) {
        benchmarkTableHeaders.push(`${capitalizeFirstLetter(measure)} (ms)`);
      }
      const benchmarkTableHeader = `<thead><tr>${benchmarkTableHeaders
        .map((header) => `<th>${header}</th>`)
        .join('')}</tr></thead>`;
      const benchmarkTableBody = `<tbody>${tableRows.join('')}</tbody>`;
      const benchmarkTable = `<table>${benchmarkTableHeader}${benchmarkTableBody}</table>`;
      const benchmarkBody = `<details><summary>${benchmarkSummary}</summary>${benchmarkTable}</details>\n\n`;
      commentBody += `${benchmarkBody}`;
    } catch (error) {
      console.error(`Error constructing benchmark results: '${error}'`);
    }
  } else {
    console.log(`No results for ${summaryPlatform} found; skipping benchmark`);
  }

  try {
    const highlights = await getHighlights({ artifactBase: BUILD_LINK_BASE });
    if (highlights) {
      const highlightsBody = `### highlights:\n${highlights}\n`;
      commentBody += highlightsBody;
    }
  } catch (error) {
    console.error(`Error constructing highlight results: '${error}'`);
  }

  const JSON_PAYLOAD = JSON.stringify({ body: commentBody });
  const POST_COMMENT_URI = `https://api.github.com/repos/metamask/metamask-extension/issues/${CIRCLE_PR_NUMBER}/comments`;
  console.log(`Announcement:\n${commentBody}`);
  console.log(`Posting to: ${POST_COMMENT_URI}`);

  const response = await fetch(POST_COMMENT_URI, {
    method: 'POST',
    body: JSON_PAYLOAD,
    headers: {
      'User-Agent': 'metamaskbot',
      Authorization: `token ${GITHUB_COMMENT_TOKEN}`,
    },
  });
  if (!response.ok) {
    throw new Error(`Post comment failed with status '${response.statusText}'`);
  }
}