/* eslint-disable @bigbinary/neeto/use-array-methods */
const fs = require("fs");
const path = require("path");

const { watch } = require("chokidar");

const {
  generateMergedTranslations,
  writeTranslationsToDisk,
} = require("../../utils/packageTranslations");

// Only watch host app translations. Package translations inside
// node_modules/@bigbinary are handled by the nanoDevelopment plugin
const getTranslationWatchPaths = () => {
  const hostTranslationsDir = path.resolve("app/javascript/src/translations");

  return fs.existsSync(hostTranslationsDir) ? [hostTranslationsDir] : [];
};

const rebuildTranslations = () => {
  const mergedTranslations = generateMergedTranslations();
  writeTranslationsToDisk(mergedTranslations);
};

const packageTranslationsPlugin = () => {
  let watcher;
  let debounceTimeout;

  return {
    name: "package-translations",
    buildStart() {
      rebuildTranslations();
    },
    handleHotUpdate({ file }) {
      // Suppress Vite's default full-reload for translation JSON files
      // so that only our plugin controls the rebuild + reload cycle.
      if (file.includes("translations") && file.endsWith(".json")) return [];

      return undefined;
    },
    configureServer(server) {
      const watchPaths = getTranslationWatchPaths();
      if (watchPaths.length === 0) return;

      server.watcher.unwatch(watchPaths);
      server.httpServer?.on("close", () => {
        if (!watcher) return;

        watcher.close();
        watcher = null;
      });

      const invalidateAllModules = () => {
        const moduleGraph = server.moduleGraph;

        for (const mod of moduleGraph.idToModuleMap.values()) {
          moduleGraph.invalidateModule(mod);
        }
      };

      const handleChange = () => {
        clearTimeout(debounceTimeout);
        debounceTimeout = setTimeout(() => {
          try {
            const mergedTranslations = generateMergedTranslations();
            writeTranslationsToDisk(mergedTranslations);

            // Invalidate all modules so the preval transform re-runs and
            // all import URLs are regenerated with the new browserHash.
            invalidateAllModules();

            // Update the browserHash on every optimized dep and chunk entry
            // so the ?v= param changes in import URLs, forcing the browser
            // to bypass its immutable cache for pre-bundled deps.
            const depsOptimizer = server.environments?.client?.depsOptimizer;
            if (depsOptimizer?.metadata) {
              const newHash = Date.now().toString(36);
              const { metadata } = depsOptimizer;
              metadata.browserHash = newHash;

              for (const entry of Object.values(metadata.optimized)) {
                entry.browserHash = newHash;
              }

              for (const entry of Object.values(metadata.chunks)) {
                entry.browserHash = newHash;
              }
            }

            server.hot.send({ type: "full-reload" });
          } catch (error) {
            server.config.logger.error(
              `[package-translations] Rebuild failed: ${error}`
            );
          }
        }, 1000);
      };

      watcher = watch(watchPaths, { ignoreInitial: true })
        .on("add", handleChange)
        .on("change", handleChange)
        .on("unlink", handleChange)
        .on("error", error =>
          server.config.logger.error(
            `[package-translations] Watcher error: ${error}`
          )
        );
    },
  };
};

module.exports = { packageTranslationsPlugin };
