/* eslint-disable security/detect-non-literal-fs-filename */
import fs from "fs";
import { createRequire } from "module";
import path from "path";

import { globby } from "globby";
import copy from "rollup-plugin-copy";

const require = createRequire(import.meta.url);

const getAppArg = () => {
  const argv = process.argv.slice(2);

  for (let i = 0; i < argv.length; i++) {
    if (argv[i] === "--app" && argv[i + 1] && !argv[i + 1].startsWith("--")) {
      return argv[i + 1];
    }

    if (argv[i].startsWith("--app=")) {
      return argv[i].split("=")[1];
    }
  }

  return null;
};

const invalidateYarnCache = destination => {
  const nodeModulesDir = path.resolve(destination, "../..");

  // Yarn 1.x uses .yarn-integrity
  const integrityPath = path.join(nodeModulesDir, ".yarn-integrity");
  if (fs.existsSync(integrityPath)) fs.unlinkSync(integrityPath);

  // Yarn 4.x (with nodeLinker: node-modules) uses .yarn-state.yml
  const statePath = path.join(nodeModulesDir, ".yarn-state.yml");
  if (fs.existsSync(statePath)) fs.unlinkSync(statePath);
};

const emitReadyFile = (
  destination,
  { shouldInvalidateYarnCache = false } = {}
) => ({
  name: "emit-ready-file",
  writeBundle: () => {
    const markerPath = path.join(destination, ".ready");

    require("fs").writeFileSync(
      markerPath,
      `Built at ${new Date().toISOString()}`
    );

    if (shouldInvalidateYarnCache) invalidateYarnCache(destination);
  },
});

const watchPaths = customWatchPaths => ({
  name: "watch-translations",
  async buildStart() {
    const pathsToWatch = [];

    if (fs.existsSync("app/javascript/src/translations")) {
      pathsToWatch.push("app/javascript/src/translations");
    } else if (fs.existsSync("src/translations")) {
      pathsToWatch.push("src/translations");
    }

    if (fs.existsSync("typeTemplates")) {
      pathsToWatch.push("typeTemplates/**/*");
    } else {
      pathsToWatch.push("types/**/*");
      pathsToWatch.push("*.d.ts");
    }

    if (customWatchPaths && customWatchPaths.length) {
      pathsToWatch.push(...customWatchPaths);
    }

    const matchedPaths = await globby(pathsToWatch);

    matchedPaths.forEach(p => this.addWatchFile(path.resolve(p)));
  },
});

const copyFiles = (destination, customCopyPaths) =>
  copy({
    targets: [
      { src: "package.json", dest: destination },
      { src: "*.d.ts", dest: destination },
      { src: "types/", dest: destination },
      { src: "LICENSE.md", dest: destination },
      { src: "src/translations", dest: path.join(destination, "src") },
      {
        src: "app/javascript/src/translations",
        dest: path.join(destination, "app/javascript/src"),
      },
      ...customCopyPaths.map(p => ({
        src: p.src,
        dest: path.join(destination, p.dest),
      })),
    ],
  });

const getWatchConfig = ({
  customCopyPaths = [],
  customWatchPaths = [],
} = {}) => {
  const appArg = getAppArg();

  const currentDir = process.cwd();

  if (!appArg) {
    return {
      watchPlugins: [emitReadyFile(path.resolve(currentDir, "dist"))],
      appPath: null,
    };
  }

  const packageJson = require(path.resolve(currentDir, "package.json"));

  const appPath = path.resolve(
    currentDir,
    appArg,
    "node_modules",
    packageJson.name
  );

  const watchPlugins = [
    watchPaths(customWatchPaths),
    copyFiles(appPath, customCopyPaths),
    emitReadyFile(appPath, { shouldInvalidateYarnCache: true }),
  ];

  return { watchPlugins, appPath };
};

export { getWatchConfig };
