#!/usr/bin/env node
// @ts-check

'use strict';

import {writeFile, mkdir, copyFile} from 'fs/promises';
import {dirname, join} from 'path';
import {fileURLToPath} from 'url';

import core from '@actions/core';
import semver from 'semver';

import packageJson from '../packages/puppeteer-core/package.json' assert {type: 'json'};

const codifyAndJoinValues = values => {
  return values
    .map(value => {
      return `\`${value}\``;
    })
    .join(' ,');
};
const formatMessage = value => {
  return value.trim();
};
const removeVersionPrefix = value => {
  return value.startsWith('v') ? value.slice(1) : value;
};

const LAST_PUPPETEER_VERSION = packageJson.version;
if (!LAST_PUPPETEER_VERSION) {
  core.setFailed('No maintained version found.');
}
const LAST_SUPPORTED_NODE_VERSION = removeVersionPrefix(
  packageJson.engines.node.slice(2).trim()
);

const SUPPORTED_OSES = ['windows', 'macos', 'linux'];
const SUPPORTED_PACKAGE_MANAGERS = ['yarn', 'npm', 'pnpm'];

const ERROR_MESSAGES = {
  unsupportedOs(value) {
    return formatMessage(`
This issue has an unsupported OS: \`${value}\`. Only the following operating systems are supported: ${codifyAndJoinValues(
      SUPPORTED_OSES
    )}. Please verify the issue on a supported OS and update the form.
`);
  },
  unsupportedPackageManager(value) {
    return formatMessage(`
This issue has an unsupported package manager: \`${value}\`. Only the following package managers are supported: ${codifyAndJoinValues(
      SUPPORTED_PACKAGE_MANAGERS
    )}. Please verify the issue using a supported package manager and update the form.
`);
  },
  invalidPackageManagerVersion(value) {
    return formatMessage(`
This issue has an invalid package manager version: \`${value}\`. Versions must follow [SemVer](https://semver.org/) formatting. Please update the form with a valid version.
`);
  },
  unsupportedNodeVersion(value) {
    return formatMessage(`
This issue has an unsupported Node.js version: \`${value}\`. Only versions above \`v${LAST_SUPPORTED_NODE_VERSION}\` are supported. Please verify the issue on a supported version of Node.js and update the form.
`);
  },
  invalidNodeVersion(value) {
    return formatMessage(`
This issue has an invalid Node.js version: \`${value}\`. Versions must follow [SemVer](https://semver.org/) formatting. Please update the form with a valid version.
`);
  },
  unsupportedPuppeteerVersion(value) {
    return formatMessage(`
This issue has an outdated Puppeteer version: \`${value}\`. Please verify your issue on the latest \`${LAST_PUPPETEER_VERSION}\` version. Then update the form accordingly.
`);
  },
  invalidPuppeteerVersion(value) {
    return formatMessage(`
This issue has an invalid Puppeteer version: \`${value}\`. Versions must follow [SemVer](https://semver.org/) formatting. Please update the form with a valid version.
`);
  },
};

(async () => {
  let input = '';
  for await (const chunk of process.stdin.iterator({
    destroyOnReturn: false,
  })) {
    input += chunk;
  }
  input = JSON.parse(input).trim();

  let mvce = '';
  let error = '';
  let configuration = '';
  let puppeteerVersion = '';
  let nodeVersion = '';
  let packageManagerVersion = '';
  let packageManager = '';
  let os = '';
  const behavior = {};
  const lines = input.split('\n');
  {
    /** @type {(value: string) => void} */
    let set = () => {
      return void 0;
    };
    let j = 1;
    let i = 1;
    for (; i < lines.length; ++i) {
      if (lines[i].startsWith('### Bug behavior')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          if (value.match(/\[x\] Flaky/i)) {
            behavior.flaky = true;
          }
          if (value.match(/\[x\] pdf/i)) {
            behavior.noError = true;
          }
        };
      } else if (lines[i].startsWith('### Minimal, reproducible example')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          mvce = value;
        };
      } else if (lines[i].startsWith('### Error string')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          if (value.match(/no error/i)) {
            behavior.noError = true;
          } else {
            error = value;
          }
        };
      } else if (lines[i].startsWith('### Puppeteer configuration')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          configuration = value;
        };
      } else if (lines[i].startsWith('### Puppeteer version')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          puppeteerVersion = removeVersionPrefix(value);
        };
      } else if (lines[i].startsWith('### Node version')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          nodeVersion = removeVersionPrefix(value);
        };
      } else if (lines[i].startsWith('### Package manager version')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          packageManagerVersion = removeVersionPrefix(value);
        };
      } else if (lines[i].startsWith('### Package manager')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          packageManager = value.toLowerCase();
        };
      } else if (lines[i].startsWith('### Operating system')) {
        set(lines.slice(j, i).join('\n').trim());
        j = i + 1;
        set = value => {
          os = value.toLowerCase();
        };
      }
    }
    set(lines.slice(j, i).join('\n').trim());
  }

  let runsOn;
  switch (os) {
    case 'windows':
      runsOn = 'windows-latest';
      break;
    case 'macos':
      runsOn = 'macos-latest';
      break;
    case 'linux':
      runsOn = 'ubuntu-latest';
      break;
    default:
      core.setOutput('errorMessage', ERROR_MESSAGES.unsupportedOs(os));
      core.setFailed(`Unsupported OS: ${os}`);
  }

  if (!SUPPORTED_PACKAGE_MANAGERS.includes(packageManager)) {
    core.setOutput(
      'errorMessage',
      ERROR_MESSAGES.unsupportedPackageManager(packageManager)
    );
    core.setFailed(`Unsupported package manager: ${packageManager}`);
  }

  if (!semver.valid(nodeVersion)) {
    core.setOutput(
      'errorMessage',
      ERROR_MESSAGES.invalidNodeVersion(nodeVersion)
    );
    core.setFailed('Invalid Node version');
  }
  if (semver.lt(nodeVersion, LAST_SUPPORTED_NODE_VERSION)) {
    core.setOutput(
      'errorMessage',
      ERROR_MESSAGES.unsupportedNodeVersion(nodeVersion)
    );
    core.setFailed(`Unsupported node version: ${nodeVersion}`);
  }

  if (!semver.valid(puppeteerVersion)) {
    core.setOutput(
      'errorMessage',
      ERROR_MESSAGES.invalidPuppeteerVersion(puppeteerVersion)
    );
    core.setFailed(`Invalid puppeteer version: ${puppeteerVersion}`);
  }
  if (
    !LAST_PUPPETEER_VERSION ||
    semver.lt(puppeteerVersion, LAST_PUPPETEER_VERSION)
  ) {
    core.setOutput(
      'errorMessage',
      ERROR_MESSAGES.unsupportedPuppeteerVersion(puppeteerVersion)
    );
    core.setFailed(`Unsupported puppeteer version: ${puppeteerVersion}`);
  }

  if (!semver.valid(packageManagerVersion)) {
    core.setOutput(
      'errorMessage',
      ERROR_MESSAGES.invalidPackageManagerVersion(packageManagerVersion)
    );
    core.setFailed(`Invalid package manager version: ${packageManagerVersion}`);
  }

  core.setOutput('errorMessage', '');
  core.setOutput('runsOn', runsOn);
  core.setOutput('nodeVersion', nodeVersion);
  core.setOutput('packageManager', packageManager);

  await mkdir('out');
  Promise.all([
    writeFile(join('out', 'main.ts'), mvce.split('\n').slice(1, -1).join('\n')),
    writeFile(join('out', 'puppeteer-error.txt'), error),
    writeFile(
      join('out', 'puppeteer.config.js'),
      configuration.split('\n').slice(1, -1).join('\n')
    ),
    writeFile(join('out', 'puppeteer-behavior.json'), JSON.stringify(behavior)),
    writeFile(
      join('out', 'package.json'),
      JSON.stringify({
        packageManager: `${packageManager}@${packageManagerVersion}`,
        scripts: {
          start: 'tsx main.ts',
          verify: 'tsx verify_issue.ts',
        },
        dependencies: {
          puppeteer: puppeteerVersion,
        },
        devDependencies: {
          tsx: 'latest',
        },
      })
    ),
    copyFile(
      join(
        dirname(fileURLToPath(import.meta.url)),
        'assets',
        'verify_issue.ts'
      ),
      join('out', 'verify_issue.ts')
    ),
  ]);
})();
