import { Plugin, Compiler, BannerPlugin, DefinePlugin } from 'webpack';
import { setEnvironment, getDefineVariables, getVariables } from './helpers';

const pluginName = 'PiletWebpackPlugin';

export interface PiletWebpackPluginOptions {
  /**
   * The name of the pilet.
   */
  name: string;
  /**
   * The version of the pilet.
   */
  version: string;
  /**
   * The name of the Piral instance / app shell.
   */
  piral: string;
  /**
   * The schema version. By default, v1 is used.
   */
  schema?: 'v0' | 'v1';
  /**
   * The shared dependencies. By default, these are read from the
   * Piral instance.
   */
  externals?: Array<string>;
  /**
   * Additional environment variables to define.
   */
  variables?: Record<string, string>;
}

function getExternals(piral: string) {
  const shellPkg = require(`${piral}/package.json`);
  const piralExternals = shellPkg.pilets?.externals ?? [];
  return [
    ...piralExternals,
    '@dbeining/react-atom',
    '@libre/atom',
    'history',
    'react',
    'react-dom',
    'react-router',
    'react-router-dom',
    'tslib',
    'path-to-regexp',
  ];
}

export class PiletWebpackPlugin implements Plugin {
  constructor(private options: PiletWebpackPluginOptions) {}

  apply(compiler: Compiler) {
    const environment = process.env.NODE_ENV || 'development';
    const { name, version, piral, externals = getExternals(piral), schema } = this.options;
    const shortName = name.replace(/\W/gi, '');
    const jsonpFunction = `pr_${shortName}`;
    const bannerSuffix = schema ? `1(${jsonpFunction})` : `0`;
    const variables = {
      ...getVariables(name, version, environment),
      ...this.options.variables,
    };
    const plugins = [
      new BannerPlugin({
        banner: `//@pilet v:${bannerSuffix}`,
        entryOnly: true,
        raw: true,
      }),
      new DefinePlugin(getDefineVariables(variables)),
    ];

    setEnvironment(variables);

    plugins.forEach(plugin => plugin.apply(compiler));

    compiler.hooks.afterEnvironment.tap(pluginName, () => {
      const current = compiler.options.externals;
      compiler.options.output.jsonpFunction = `${jsonpFunction}_chunks`;
      compiler.options.output.libraryTarget = 'umd';
      compiler.options.output.library = name;

      if (schema === 'v1') {
        compiler.options.output.auxiliaryComment = {
          commonjs2: `\nfunction define(d,k){(typeof document!=='undefined')&&(document.currentScript.app=k.apply(null,d.map(window.${jsonpFunction})));}define.amd=!0;`,
        } as any;
      }

      compiler.options.externals = Array.isArray(current)
        ? [...current, ...externals]
        : current
        ? [current, ...externals]
        : externals;
    });
  }
}
