import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

import ViteYaml from "@modyfi/vite-plugin-yaml";
import inject from "@rollup/plugin-inject";
import vue from "@vitejs/plugin-vue2";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

import { buildMetadataPlugin } from "./vite-plugin-build-metadata.js";
import { galaxyDevServerPlugin } from "./vite-plugin-galaxy-dev-server.js";
import { galaxyLegacyPlugin } from "./vite-plugin-galaxy-legacy.js";

const __dirname = dirname(fileURLToPath(import.meta.url));
const packageJson = JSON.parse(readFileSync(resolve(__dirname, "package.json"), "utf-8"));

/**
 * Plugin to fix D3 v3 ES module compatibility
 * D3 v3 is an IIFE that expects `this` to be `window` at the module level.
 * In ES modules, `this` is `undefined`. This plugin wraps the code to
 * execute with the correct global context.
 */
function d3v3CompatPlugin() {
    return {
        name: "d3v3-compat",
        transform(code, id) {
            // Only transform the d3v3 main file
            if (
                (id.includes("node_modules/d3v3/d3.js") || id.includes("node_modules\\d3v3\\d3.js")) &&
                code.startsWith("!function()")
            ) {
                // The D3 v3 code is: !function() { ... }();
                // We need to change it to: !function() { ... }.call(window);
                // This ensures `this` inside the IIFE refers to `window`
                const transformed = code.replace(
                    /\}(\s*)\(\s*\)\s*;?\s*$/,
                    "}.call(typeof window !== 'undefined' ? window : globalThis);",
                );
                return {
                    code: transformed,
                    map: null,
                };
            }
            return null;
        },
    };
}

export default defineConfig({
    // Use relative base so CSS asset references work with any proxy prefix.
    // The HTML script tags use url_for() which handles the prefix correctly.
    base: "./",
    define: {
        // Make jQuery available globally for plugins and legacy code
        global: "globalThis",
        // Polyfill process.env for Node.js modules (like util) that reference it
        "process.env": JSON.stringify({}),
        // Define build system for runtime checks
        __GALAXY_BUILD_SYSTEM__: JSON.stringify("vite"),
        // Define build timestamp
        __buildTimestamp__: JSON.stringify(new Date().toISOString()),
        // Define license from package.json
        __license__: JSON.stringify(packageJson.license),
    },
    plugins: [
        vue({
            template: {
                compilerOptions: {
                    // Preserve whitespace to match Webpack's vue-loader default behavior
                    whitespace: "preserve",
                },
            },
        }),
        tsconfigPaths(), // TypeScript path resolution
        ViteYaml(), // YAML file support
        galaxyLegacyPlugin(), // Handle legacy module resolution
        buildMetadataPlugin(), // Generate build metadata (replaces DumpMetaPlugin)
        d3v3CompatPlugin(), // Fix D3 v3 ES module compatibility
        // Inject imports for underscore and Buffer
        // jQuery is set up as window.$ and window.jQuery by libs.bundled.js
        // Note: We don't inject jQuery here to avoid circular dependencies with code splitting
        inject({
            include: ["**/*.js", "**/*.ts", "**/*.vue"],
            exclude: ["**/node_modules/**"],
            _: "underscore",
            Buffer: ["buffer", "Buffer"],
        }),
        galaxyDevServerPlugin(), // Transform proxied Galaxy HTML for HMR support
    ],
    // resolve aliases are handled by galaxyLegacyPlugin
    css: {
        preprocessorOptions: {
            scss: {
                quietDeps: true,
                silenceDeprecations: ["import", "color-functions", "global-builtin"],
                includePaths: ["src/style", "src/style/scss", "node_modules"],
            },
        },
    },
    build: {
        outDir: "dist",
        emptyOutDir: true,
        // Generate manifest.json for production
        manifest: true,
        // Disable CSS code splitting - combine all CSS into one file
        cssCodeSplit: false,
        // Generate sourcemaps when GXY_BUILD_SOURCEMAPS is set
        sourcemap: !!process.env.GXY_BUILD_SOURCEMAPS,
        rollupOptions: {
            input: {
                // Entry points that will be referenced in templates
                // libs must be loaded first - it exposes globals (jQuery, bundleEntries, config)
                libs: resolve(__dirname, "src/entry/libs.js"),
                analysis: resolve(__dirname, "src/entry/analysis/index.ts"),
                generic: resolve(__dirname, "src/entry/generic.js"),
            },
            output: {
                entryFileNames: "[name].bundled.js",
                chunkFileNames: "[name]-[hash].js",
                // CSS should be named 'base.css' to match what templates expect
                assetFileNames: (assetInfo) => {
                    if (assetInfo.name && assetInfo.name.endsWith(".css")) {
                        return "base.css";
                    }
                    return "[name]-[hash].[ext]";
                },
                manualChunks: (id) => {
                    // Put jQuery in its own chunk - jquery-migrate is imported dynamically
                    if (id.includes("node_modules/jquery/") && !id.includes("jquery-migrate")) {
                        return "jquery-core";
                    }
                    // Keep app/* files together to avoid circular dependency issues
                    // (getGalaxyInstance is re-exported through app/index.js)
                    if (id.includes("/src/app/")) {
                        return "galaxy-app";
                    }
                },
            },
        },
    },
    server: {
        port: process.env.VITE_PORT || 5173,
        host: "0.0.0.0",
        proxy: {
            // Proxy everything except Vite's own routes to Galaxy backend
            "^/(?!(@|src/|node_modules/|__vite))": {
                target: process.env.GALAXY_URL || "http://127.0.0.1:8080",
                changeOrigin: !!process.env.CHANGE_ORIGIN,
                secure: process.env.CHANGE_ORIGIN ? false : true,
            },
        },
        cors: true,
        hmr: {
            protocol: "ws",
            host: "localhost",
        },
    },
    worker: {
        format: "es",
    },
    optimizeDeps: {
        // Use esbuild plugin to fix D3 v3's IIFE `this` binding during pre-bundling
        esbuildOptions: {
            plugins: [
                {
                    name: "d3v3-compat-esbuild",
                    setup(build) {
                        build.onLoad({ filter: /node_modules\/d3v3\/d3\.js$/ }, async (args) => {
                            const fs = await import("node:fs");
                            let contents = fs.readFileSync(args.path, "utf8");
                            // D3 v3 is: !function() { ... }();
                            // Change to: !function() { ... }.call(window);
                            contents = contents.replace(
                                /\}(\s*)\(\s*\)\s*;?\s*$/,
                                "}.call(typeof window !== 'undefined' ? window : globalThis);",
                            );
                            return { contents, loader: "js" };
                        });
                    },
                },
            ],
        },
    },
});
