#!/usr/bin/env node

'use strict';

const {join} = require('path');
const {readJsonSync} = require('fs-extra');
const clear = require('clear');
const {getPublicPackages, handleError} = require('./utils');
const theme = require('./theme');

const checkNPMPermissions = require('./publish-commands/check-npm-permissions');
const confirmSkippedPackages = require('./publish-commands/confirm-skipped-packages');
const confirmVersionAndTags = require('./publish-commands/confirm-version-and-tags');
const parseParams = require('./publish-commands/parse-params');
const printFollowUpInstructions = require('./publish-commands/print-follow-up-instructions');
const promptForOTP = require('./publish-commands/prompt-for-otp');
const publishToNPM = require('./publish-commands/publish-to-npm');
const updateStableVersionNumbers = require('./publish-commands/update-stable-version-numbers');
const validateTags = require('./publish-commands/validate-tags');
const validateSkipPackages = require('./publish-commands/validate-skip-packages');

const run = async () => {
  try {
    const params = parseParams();

    const version =
      params.publishVersion ??
      readJsonSync('./build/node_modules/react/package.json').version;
    const isExperimental = version.includes('experimental');

    params.cwd = join(__dirname, '..', '..');
    params.packages = await getPublicPackages(isExperimental);

    if (params.onlyPackages.length > 0 && params.skipPackages.length > 0) {
      console.error(
        '--onlyPackages and --skipPackages cannot be used together'
      );
      process.exit(1);
    }

    if (params.onlyPackages.length > 0) {
      params.packages = params.packages.filter(packageName => {
        return params.onlyPackages.includes(packageName);
      });
    }

    // Pre-filter any skipped packages to simplify the following commands.
    // As part of doing this we can also validate that none of the skipped packages were misspelled.
    params.skipPackages.forEach(packageName => {
      const index = params.packages.indexOf(packageName);
      if (index < 0) {
        console.log(
          theme`Invalid skip package {package ${packageName}} specified.`
        );
        process.exit(1);
      } else {
        params.packages.splice(index, 1);
      }
    });

    await validateTags(params);
    await confirmSkippedPackages(params);
    await confirmVersionAndTags(params);
    await validateSkipPackages(params);
    await checkNPMPermissions(params);

    const packageNames = params.packages;

    if (params.ci) {
      let failed = false;
      for (let i = 0; i < packageNames.length; i++) {
        try {
          const packageName = packageNames[i];
          await publishToNPM(params, packageName, null);
        } catch (error) {
          failed = true;
          console.error(error.message);
          console.log();
          console.log(
            theme.error`Publish failed. Will attempt to publish remaining packages.`
          );
        }
      }
      if (failed) {
        console.log(theme.error`One or more packages failed to publish.`);
        process.exit(1);
      }
    } else {
      clear();
      let otp = await promptForOTP(params);
      for (let i = 0; i < packageNames.length; ) {
        const packageName = packageNames[i];
        try {
          await publishToNPM(params, packageName, otp);
          i++;
        } catch (error) {
          console.error(error.message);
          console.log();
          console.log(
            theme.error`Publish failed. Enter a fresh otp code to retry.`
          );
          otp = await promptForOTP(params);
          // Try publishing package again
          continue;
        }
      }
      await updateStableVersionNumbers(params);
      await printFollowUpInstructions(params);
    }
  } catch (error) {
    handleError(error);
  }
};

run();
