import applySourceMap from "vinyl-sourcemaps-apply";
import { callbackify } from "util";
import commonJS from "@rollup/plugin-commonjs";
import File from "vinyl";
import logger from "gulplog";
import nodeResolver from "@rollup/plugin-node-resolve";
import { obj } from "through2";
import PluginError from "plugin-error";
import { rollup } from "rollup";

const PLUGIN_NAME = "rollup";

const DEFAULT_INPUT_OPTIONS = {
  plugins: [nodeResolver(), commonJS()],
};

const DEFAULT_OUTPUT_OPTIONS = {
  format: "iife",
};

const onwarn = (input) => {
  const path = input.relative;
  return ({ loc, code, message }) =>
    logger.warn(
      "%s: [%s] %s: %s",
      PLUGIN_NAME,
      code,
      loc ? `${path} (${loc.file}:${loc.line}:${loc.column})` : path,
      message
    );
};

export default function (inputOptions = {}, outputOptions) {
  const inOpts = { ...DEFAULT_INPUT_OPTIONS, ...inputOptions };
  const outOpts = { ...DEFAULT_OUTPUT_OPTIONS, ...outputOptions };

  return obj(
    callbackify(
      /**
       * @param {File} input
       */
      async function (input) {
        if (input.extname !== ".js") {
          this.push(input);
          return;
        }
        try {
          const bundle = await rollup({
            ...inOpts,
            input: input.path,
            onwarn: onwarn(input),
          });

          const { output } = await bundle.generate({
            ...outOpts,
            sourcemap: !!input.sourceMap,
          });

          for (const chunk of output) {
            const fileChunk = new File({
              base: input.base,
              path: input.path,
              history: input.history,
            });
            fileChunk.sourceMap = input.sourceMap;
            fileChunk.basename = chunk.fileName;
            if (input.sourceMap && chunk.map) {
              applySourceMap(fileChunk, chunk.map);
            }
            switch (chunk.type) {
              case "chunk":
                fileChunk.contents = Buffer.from(chunk.code);
                break;
              case "asset":
                fileChunk.contents = Buffer.from(chunk.source);
                break;
            }
            this.push(fileChunk);
          }
        } catch (error) {
          throw new PluginError(PLUGIN_NAME, error);
        }
      }
    )
  );
}