/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 * @oncall react_native
 */

import type {RunBuildOptions} from '../index';
import type {CustomTransformOptions} from 'metro-babel-transformer';
import type {CustomResolverOptions} from 'metro-resolver';
import type {ModuleObject} from 'yargs';
import typeof Yargs from 'yargs';

import parseKeyValueParamArray from '../cli/parseKeyValueParamArray';

const {makeAsyncCommand} = require('../cli-utils');
const TerminalReporter = require('../lib/TerminalReporter');
const {loadConfig} = require('metro-config');
const {Terminal} = require('metro-core');

const term = new Terminal(process.stdout);
const updateReporter = new TerminalReporter(term);

type Args = $ReadOnly<{
  config?: string,
  dev?: boolean,
  entry: string,
  legacyBundler?: boolean,
  maxWorkers?: number,
  minify?: boolean,
  out: string,
  outputType?: string,
  platform?: string,
  projectRoots?: $ReadOnlyArray<string>,
  resetCache?: boolean,
  sourceMap?: boolean,
  sourceMapUrl?: string,
  transformOption: CustomTransformOptions,
  resolverOption: CustomResolverOptions,
}>;

module.exports = (): {
  ...ModuleObject,
  handler: Function,
} => ({
  command: 'build <entry>',
  desc: 'Generates a JavaScript bundle containing the specified entrypoint and its descendants',

  builder: (yargs: Yargs): void => {
    yargs.option('project-roots', {
      alias: 'P',
      type: 'string',
      array: true,
    });
    yargs.option('out', {alias: 'O', type: 'string', demandOption: true});

    yargs.option('platform', {alias: 'p', type: 'string'});
    yargs.option('output-type', {alias: 't', type: 'string'});

    yargs.option('max-workers', {alias: 'j', type: 'number'});

    yargs.option('minify', {alias: 'z', type: 'boolean'});
    yargs.option('dev', {alias: 'g', type: 'boolean'});

    yargs.option('source-map', {type: 'boolean'});
    yargs.option('source-map-url', {type: 'string'});

    yargs.option('legacy-bundler', {type: 'boolean'});

    yargs.option('config', {alias: 'c', type: 'string'});

    yargs.option('transform-option', {
      type: 'string',
      array: true,
      alias: 'transformer-option',
      coerce: (parseKeyValueParamArray: $FlowFixMe),
      describe:
        'Custom transform options of the form key=value. URL-encoded. May be specified multiple times.',
    });

    yargs.option('resolver-option', {
      type: 'string',
      array: true,
      coerce: (parseKeyValueParamArray: $FlowFixMe),
      describe:
        'Custom resolver options of the form key=value. URL-encoded. May be specified multiple times.',
    });

    // Deprecated
    yargs.option('reset-cache', {type: 'boolean'});
  },

  handler: makeAsyncCommand(async (argv: Args) => {
    const config = await loadConfig(argv);
    const options: RunBuildOptions = {
      entry: argv.entry,
      dev: argv.dev,
      out: argv.out,
      minify: argv.minify,
      platform: argv.platform,
      sourceMap: argv.sourceMap,
      sourceMapUrl: argv.sourceMapUrl,
      customResolverOptions: argv.resolverOption,
      customTransformOptions: argv.transformOption,
    };

    // Inline require() to avoid circular dependency with ../index
    const MetroApi = require('../index');

    await MetroApi.runBuild(config, {
      ...options,
      onBegin: (): void => {
        updateReporter.update({
          buildID: '$',
          type: 'bundle_build_started',
          bundleDetails: {
            bundleType: 'Bundle',
            customResolverOptions: options.customResolverOptions ?? {},
            customTransformOptions: options.customTransformOptions ?? {},
            dev: !!options.dev,
            entryFile: options.entry,
            minify: !!options.minify,
            platform: options.platform,
          },
        });
      },
      onProgress: (
        transformedFileCount: number,
        totalFileCount: number,
      ): void => {
        updateReporter.update({
          buildID: '$',
          type: 'bundle_transform_progressed',
          transformedFileCount,
          totalFileCount,
        });
      },
      onComplete: (): void => {
        updateReporter.update({
          buildID: '$',
          type: 'bundle_build_done',
        });
      },
    });
  }),
});