class CLIPlugin {
  constructor(options) {
    this.options = options;
  }

  setupHotPlugin(compiler) {
    const { HotModuleReplacementPlugin } = compiler.webpack || require("webpack");
    const hotModuleReplacementPlugin = Boolean(
      compiler.options.plugins.find((plugin) => plugin instanceof HotModuleReplacementPlugin),
    );

    if (!hotModuleReplacementPlugin) {
      new HotModuleReplacementPlugin().apply(compiler);
    }
  }

  setupPrefetchPlugin(compiler) {
    const { PrefetchPlugin } = compiler.webpack || require("webpack");

    new PrefetchPlugin(null, this.options.prefetch).apply(compiler);
  }

  async setupBundleAnalyzerPlugin(compiler) {
    // eslint-disable-next-line node/no-extraneous-require
    const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
    const bundleAnalyzerPlugin = Boolean(
      compiler.options.plugins.find((plugin) => plugin instanceof BundleAnalyzerPlugin),
    );

    if (!bundleAnalyzerPlugin) {
      new BundleAnalyzerPlugin().apply(compiler);
    }
  }

  setupProgressPlugin(compiler) {
    const { ProgressPlugin } = compiler.webpack || require("webpack");
    const progressPlugin = Boolean(
      compiler.options.plugins.find((plugin) => plugin instanceof ProgressPlugin),
    );

    if (!progressPlugin) {
      new ProgressPlugin({
        profile: this.options.progress === "profile",
      }).apply(compiler);
    }
  }

  setupHelpfulOutput(compiler) {
    const pluginName = "webpack-cli";
    const getCompilationName = () => (compiler.name ? `'${compiler.name}'` : "");
    const logCompilation = (message) => {
      if (process.env.WEBPACK_CLI_START_FINISH_FORCE_LOG) {
        process.stderr.write(message);
      } else {
        this.logger.log(message);
      }
    };

    const { configPath } = this.options;

    compiler.hooks.run.tap(pluginName, () => {
      const name = getCompilationName();

      logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `);

      if (configPath) {
        this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`);
      }
    });

    compiler.hooks.watchRun.tap(pluginName, (compiler) => {
      const { bail, watch } = compiler.options;

      if (bail && watch) {
        this.logger.warn(
          'You are using "bail" with "watch". "bail" will still exit webpack when the first error is found.',
        );
      }

      const name = getCompilationName();

      logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `);

      if (configPath) {
        this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`);
      }
    });

    compiler.hooks.invalid.tap(pluginName, (filename, changeTime) => {
      const date = new Date(changeTime * 1000);

      this.logger.log(`File '${filename}' was modified`);
      this.logger.log(`Changed time is ${date} (timestamp is ${changeTime})`);
    });

    (compiler.webpack ? compiler.hooks.afterDone : compiler.hooks.done).tap(pluginName, () => {
      const name = getCompilationName();

      logCompilation(`Compiler${name ? ` ${name}` : ""} finished`);

      process.nextTick(() => {
        if (compiler.watchMode) {
          this.logger.log(`Compiler${name ? `${name}` : ""} is watching files for updates...`);
        }
      });
    });
  }

  apply(compiler) {
    this.logger = compiler.getInfrastructureLogger("webpack-cli");

    if (this.options.progress) {
      this.setupProgressPlugin(compiler);
    }

    if (this.options.hot) {
      this.setupHotPlugin(compiler);
    }

    if (this.options.prefetch) {
      this.setupPrefetchPlugin(compiler);
    }

    if (this.options.analyze) {
      this.setupBundleAnalyzerPlugin(compiler);
    }

    this.setupHelpfulOutput(compiler);
  }
}

module.exports = CLIPlugin;
