import {Command, Option, UsageError} from 'clipanion';
import {mkdir}                       from 'fs/promises';
import path                          from 'path';

import * as folderUtils              from '../folderUtils';

import {BaseCommand}                 from './Base';

export class PackCommand extends BaseCommand {
  static paths = [
    [`pack`],
  ];

  static usage = Command.Usage({
    description: `Store package managers in a tarball`,
    details: `
      Download the selected package managers and store them inside a tarball suitable for use with \`corepack install -g\`.
    `,
    examples: [[
      `Pack the package manager defined in the package.json file`,
      `corepack pack`,
    ], [
      `Pack the latest version of Yarn 1.x inside a file named corepack.tgz`,
      `corepack pack yarn@^1`,
    ]],
  });

  json = Option.Boolean(`--json`, false, {
    description: `If true, the path to the generated tarball will be printed on stdout`,
  });

  output = Option.String(`-o,--output`, {
    description: `Where the tarball should be generated; by default "corepack.tgz"`,
  });

  patterns = Option.Rest();

  async execute() {
    const descriptors = await this.resolvePatternsToDescriptors({
      patterns: this.patterns,
    });

    const installLocations: Array<string> = [];

    for (const descriptor of descriptors) {
      const resolved = await this.context.engine.resolveDescriptor(descriptor, {allowTags: true, useCache: false});
      if (resolved === null)
        throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`);

      this.context.stdout.write(`Adding ${resolved.name}@${resolved.reference} to the cache...\n`);
      const packageManagerInfo = await this.context.engine.ensurePackageManager(resolved);

      await this.context.engine.activatePackageManager(packageManagerInfo.locator);
      installLocations.push(packageManagerInfo.location);
    }

    const baseInstallFolder = folderUtils.getInstallFolder();
    const outputPath = path.resolve(this.context.cwd, this.output ?? `corepack.tgz`);

    if (!this.json) {
      this.context.stdout.write(`\n`);
      this.context.stdout.write(`Packing the selected tools in ${path.basename(outputPath)}...\n`);
    }

    const {create: tarC} = await import(`tar/create`);

    // Recreate the folder in case it was deleted somewhere else:
    await mkdir(baseInstallFolder, {recursive: true});
    await tarC({gzip: true, cwd: baseInstallFolder, file: path.resolve(outputPath)}, installLocations.map(location => {
      return path.relative(baseInstallFolder, location);
    }));

    if (this.json) {
      this.context.stdout.write(`${JSON.stringify(outputPath)}\n`);
    } else {
      this.context.stdout.write(`All done!\n`);
    }
  }
}
