/**
 * Vite Configuration for Shared FlowDrop Library
 *
 * Builds an ES module with code splitting for use in Drupal.
 * Heavy optional features (CodeMirror, EasyMDE) are split into
 * separate chunks that are lazy-loaded via dynamic import().
 *
 * Output:
 *   build/flowdrop.es.js       — main entry chunk
 *   build/flowdrop-*.js        — lazy-loaded chunks (code editors, markdown editor)
 *   build/flowdrop.css         — bundled styles
 *
 * Global: window.FlowDrop (assigned by main.ts entry wrapper)
 */

import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import path from "path";
import fs from "fs";
import type { Plugin } from "vite";

const flowdropPkg = JSON.parse(
  fs.readFileSync(
    path.resolve("./node_modules/@flowdrop/flowdrop/package.json"),
    "utf-8",
  ),
);
const flowdropVersion = flowdropPkg.version;

/**
 * Rollup plugin that appends ?v=<version> to all inter-chunk import paths.
 *
 * Drupal's asset system adds ?v=<version> to the entry script URL, but
 * dynamic import() paths inside chunks are bare (e.g. "./flowdrop.es.js").
 * The browser treats "flowdrop.es.js?v=0.0.59" and "flowdrop.es.js" as
 * different module URLs, causing a duplicate fetch of the main bundle.
 *
 * This plugin rewrites relative .js imports in every output chunk so that
 * all inter-chunk references carry the same version query string Drupal uses.
 */
function chunkVersionPlugin(): Plugin {
  const suffix = `?v=${flowdropVersion}`;
  return {
    name: "flowdrop-chunk-version",
    renderChunk(code) {
      // Match all inter-chunk import forms:
      //   from "./flowdrop-foo.js"       — static named import
      //   import("./flowdrop-foo.js")     — dynamic import
      //   import "./flowdrop-foo.js"      — bare side-effect import
      const rewritten = code.replace(
        /(["'])(\.\/flowdrop[^"']*\.js)\1/g,
        (_match, quote, path) => `${quote}${path}${suffix}${quote}`,
      );
      if (rewritten !== code) {
        return { code: rewritten, map: null };
      }
      return null;
    },
  };
}

/**
 * Rollup plugin that redirects imports of the full FormField component
 * (which statically bundles CodeMirror) to the light/registry-based version.
 * This allows CodeMirror to be lazy-loaded as a separate chunk.
 *
 * Uses this.resolve() to catch ANY import path that resolves to
 * FormField.svelte (e.g., './FormField.svelte', '../components/form/FormField.svelte')
 * so that `export * from "@flowdrop/flowdrop/form"` is safe — the FormFieldFull
 * re-export gets redirected to FormFieldLight automatically.
 */
function formFieldLightPlugin(): Plugin {
  const fullFormField = path.resolve(
    "./node_modules/@flowdrop/flowdrop/dist/components/form/FormField.svelte",
  );
  const lightFormField = path.resolve(
    "./node_modules/@flowdrop/flowdrop/dist/components/form/FormFieldLight.svelte",
  );

  return {
    name: "flowdrop-formfield-light",
    enforce: "pre",
    async resolveId(source, importer, options) {
      if (!importer) return null;

      // Only intercept imports from within @flowdrop/flowdrop
      if (!importer.includes("@flowdrop/flowdrop")) return null;

      // Quick filter: skip anything that clearly isn't FormField-related
      if (!source.includes("FormField") && !source.endsWith("FormField.svelte"))
        return null;

      // Skip if already pointing to the light version
      if (source.includes("FormFieldLight")) return null;

      // Resolve the import to its absolute path, then check if it's
      // the heavy FormField.svelte that pulls in CodeMirror
      const resolved = await this.resolve(source, importer, {
        ...options,
        skipSelf: true,
      });
      if (resolved && resolved.id === fullFormField) {
        return lightFormField;
      }

      return null;
    },
  };
}

export default defineConfig({
  plugins: [
    chunkVersionPlugin(),
    formFieldLightPlugin(),
    svelte({
      compilerOptions: {
        dev: false,
      },
      onwarn: (warning, handler) => {
        if (warning.filename?.includes("node_modules")) {
          return;
        }
        handler(warning);
      },
    }),
  ],
  resolve: {
    alias: {
      $lib: path.resolve("./src/lib"),
      "$app/stores": path.resolve("./src/lib/mocks/app-stores.js"),
      "$app/forms": path.resolve("./src/lib/mocks/app-forms.js"),
      "$app/navigation": path.resolve("./src/lib/mocks/app-navigation.js"),
      "$app/environment": path.resolve("./src/lib/mocks/app-environment.js"),
      // Alias for internal @flowdrop/flowdrop dist paths, bypassing the
      // package.json exports restriction. This allows importing settings/theme
      // without going through the main entry (which pulls in FormFieldFull → CodeMirror).
      "flowdrop-internal": path.resolve(
        "./node_modules/@flowdrop/flowdrop/dist",
      ),
    },
    conditions: ["svelte", "browser", "import"],
    dedupe: ["svelte"],
  },
  build: {
    // Disable Vite's module preload injection. The preload helper resolves
    // chunk URLs from the site root ("/flowdrop-full.js") which 404s in
    // Drupal where files live under /modules/.../build/. The actual
    // dynamic import() paths resolve correctly relative to the module URL.
    modulePreload: false,
    outDir: "build",
    rollupOptions: {
      input: "src/lib/main.ts",
      output: {
        format: "es",
        entryFileNames: "flowdrop.es.js",
        chunkFileNames(chunkInfo) {
          // Rename chunks to stable, predictable names.
          // Drupal libraries.yml references these by name, so they
          // must be deterministic across builds.
          const ids = chunkInfo.moduleIds ?? [];
          if (ids.some((id) => id.includes("/editor/index."))) {
            return "flowdrop-editor.js";
          }
          if (ids.some((id) => id.includes("/playground/index."))) {
            return "flowdrop-playground.js";
          }
          if (ids.some((id) => id.includes("/form/code"))) {
            return "flowdrop-code.js";
          }
          if (ids.some((id) => id.includes("/form/markdown"))) {
            return "flowdrop-markdown.js";
          }
          if (ids.some((id) => id.includes("/form/full"))) {
            return "flowdrop-form-full.js";
          }
          // Normalize PascalCase/camelCase names to kebab-case.
          const name = chunkInfo.name
            .replace(/([a-z])([A-Z])/g, "$1-$2")
            .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2")
            .toLowerCase();
          return `flowdrop-${name}.js`;
        },
        assetFileNames: (assetInfo) => {
          if (assetInfo.name?.endsWith(".css")) {
            return "flowdrop.css";
          }
          return assetInfo.name || "assets/[name]-[hash][extname]";
        },
      },
    },
    // Keep all CSS in a single file — Drupal loads CSS via libraries.yml,
    // and dynamic JS chunks don't need separate CSS loading.
    cssCodeSplit: false,
    sourcemap: true,
    minify: "terser",
    terserOptions: {
      compress: {
        drop_debugger: true,
        pure_funcs: ["console.log", "console.debug", "console.info"],
      },
    },
    target: "es2020",
    chunkSizeWarningLimit: 5000,
    commonjsOptions: {
      include: [/node_modules/],
      transformMixedEsModules: true,
    },
  },
  define: {
    "process.env.NODE_ENV": JSON.stringify("production"),
    "process.env": "{}",
    "import.meta.env.MODE": JSON.stringify("production"),
    "import.meta.env.DEV": "false",
    "import.meta.env.PROD": "true",
    "import.meta.env.SSR": "false",
  },
  envPrefix: "__FLOWDROP_DISABLED__",
  optimizeDeps: {
    include: [
      "@flowdrop/flowdrop",
      "svelte",
      "@xyflow/svelte",
      "@iconify/svelte",
      "uuid",
    ],
    exclude: [],
  },
});
