/*
 * Copyright (C) 2018-2022 Open StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {SCThingType} from '@openstapps/core';
import {Logger} from '@openstapps/logger';
import {Command} from 'commander';
import {URL} from 'url';
import waitOn from 'wait-on';
import {HttpClient} from '@openstapps/api';
import {copy} from './copy.js';
import {version} from '../package.json';
// eslint-disable-next-line unicorn/prevent-abbreviations
import {e2eRun} from './e2e.js';

process.on('unhandledRejection', async error => {
  await Logger.error('unhandledRejection', error);
});

const client = new HttpClient();
const commander = new Command();
const helpAndExit = (help: string) => {
  // eslint-disable-next-line no-console
  console.log(help);
  process.exit(-1);
};

commander
  .command('e2e <to>')
  .version(version)
  .description(
    'Run in end to end test mode. Indexing and afterwards retrieving all test files from @openstapp/core to the backend',
  )
  .option(
    '-s --samples [path]',
    'Path to @openstapp/core test files',
    './node_modules/@openstapps/core/test/resources/indexable',
  )
  .option('-w --waiton [resource]', 'wait-on resource parameter see "www.npmjs.com/wait-on"')
  .option('-r --reportPath [reportPath]', 'JUnit Report Path')
  // eslint-disable-next-line unicorn/prevent-abbreviations
  .action(async (to, e2eCommand) => {
    let toURL = '';
    // validate url
    try {
      toURL = new URL(to).toString();
    } catch (error) {
      await Logger.error('expected parameter <to> to be valid url', error);
      helpAndExit(e2eCommand.helpInformation());
    }

    try {
      if (typeof e2eCommand.waiton === 'string') {
        Logger.info(`Waiting for availibilty of resource: ${e2eCommand.waiton}`);
        await waitOn({
          resources: [e2eCommand.waiton],
          timeout: 300_000,
        });
        Logger.info(`Resource became available`);
      }
      await e2eRun(client, {
        to: toURL,
        samplesLocation: e2eCommand.samples,
        reportLocation: e2eCommand.reportPath,
      });
      Logger.ok('Done');
    } catch (error) {
      await Logger.error(error);
    }
  });

commander
  .command('copy <type> <from> <to> <batchSize>')
  .version(version)
  .description('Copy data from one instance to another')
  .option(
    '-s, --bulkSource <bulkSource>',
    'The source identifier for the bulk to use with the target instance [copy]',
    'copy',
  )
  .option('-a, --appVersion <version>', 'The App version to use [unset by default]', version)
  .allowUnknownOption(false)
  .action(async (type, from, to, batchSize, copyCommand) => {
    // validate type
    if (typeof type !== 'string') {
      await Logger.error('expected parameter "type" to be of type: string');
      copyCommand.help();
      helpAndExit(copyCommand.helpInformation());
    }

    let fromURL = '';
    let toURL = '';

    // validate urls
    try {
      fromURL = new URL(from).toString();
      toURL = new URL(to).toString();
    } catch (error) {
      await Logger.error('expected parameters "from" and "to" to be valid urls', error);
      helpAndExit(copyCommand.helpInformation());
    }

    // validate batchSize
    if (Number.isNaN(Number.parseInt(batchSize, 10))) {
      await Logger.error('expected parameter "batchSize" to be of type: number');
      helpAndExit(copyCommand.helpInformation());
    }

    /**
     * Copy a single type from one backend to another
     */
    async function copySingleType(type: string) {
      Logger.info(`Copying ${type} objects from ${fromURL} to ${toURL}`);

      await copy(client, {
        batchSize: Number.parseInt(batchSize, 10),
        from: fromURL,
        source: copyCommand.bulkSource,
        to: toURL,
        type: type as SCThingType,
        version: copyCommand.appVersion,
      });
      Logger.ok('done');
    }

    if (type === '*') {
      const types = Object.values(SCThingType);
      for (const [i, type] of types.entries()) {
        Logger.info(`Copying ${type} (${i + 1} / ${types.length})`);
        try {
          await copySingleType(type);
        } catch (error) {
          await Logger.error(error);
        }
      }
    } else {
      await copySingleType(type);
    }
  });

commander.parse(process.argv);
