import { Agent } from "node:http";
import { fileURLToPath, URL } from "node:url";

import vue from "@vitejs/plugin-vue";
import { defineConfig, loadEnv, type Plugin, type ProxyOptions } from "vite";

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), "");
  const nativeApiUrl = String(process.env.WORKBENCH_NATIVE_API_URL ?? env.WORKBENCH_NATIVE_API_URL ?? "").trim();
  const nativeRuntimeConfig = String(process.env.WORKBENCH_WEB_RUNTIME_CONFIG ?? env.WORKBENCH_WEB_RUNTIME_CONFIG ?? "").trim();
  const nativeCaseRunEnabled = (process.env.HWLAB_CASERUN_NATIVE_TEST ?? env.HWLAB_CASERUN_NATIVE_TEST) === "1";
  const nativeCaseRunTarget = String(process.env.HWLAB_CASERUN_NATIVE_URL ?? env.HWLAB_CASERUN_NATIVE_URL ?? "").trim();
  const nativeCaseRunAgent = new Agent({ keepAlive: true });

  if (nativeApiUrl && nativeCaseRunEnabled) {
    throw new Error("Workbench and CaseRun native modes cannot share one Vite server");
  }
  if (nativeCaseRunEnabled && !nativeCaseRunTarget) throw new Error("HWLAB_CASERUN_NATIVE_URL is required from the CLI-managed YAML profile in CaseRun native mode");
  if (nativeApiUrl && !nativeRuntimeConfig) throw new Error("WORKBENCH_WEB_RUNTIME_CONFIG is required in Workbench native mode");
  const proxy: Record<string, string | ProxyOptions> | undefined = nativeCaseRunEnabled
    ? {
        "/auth": { target: nativeCaseRunTarget, changeOrigin: false, agent: nativeCaseRunAgent },
        "/v1/caserun": { target: nativeCaseRunTarget, changeOrigin: false, agent: nativeCaseRunAgent }
      }
    : nativeApiUrl
      ? {
          "/v1": { target: nativeApiUrl, changeOrigin: false },
          "/auth": { target: nativeApiUrl, changeOrigin: false },
          "/health": { target: nativeApiUrl, changeOrigin: false }
        }
      : undefined;

  return {
    cacheDir: nativeViteCacheDir({ nativeApiUrl, nativeCaseRunEnabled }),
    plugins: [
      ...(nativeApiUrl ? [nativeRuntimeConfigPlugin(nativeRuntimeConfig)] : []),
      ...(nativeCaseRunEnabled ? [nativeCaseRunRuntimeConfigPlugin()] : []),
      vue()
    ],
    resolve: {
      alias: {
        "@": fileURLToPath(new URL("./src", import.meta.url))
      }
    },
    server: {
      host: env.WORKBENCH_WEB_HOST || "127.0.0.1",
      port: Number(env.WORKBENCH_WEB_PORT || 5173),
      strictPort: true,
      watch: nativeCaseRunEnabled || env.WORKBENCH_VITE_USE_POLLING === "1"
        ? { usePolling: true, interval: nativeCaseRunEnabled ? 300 : 500 }
        : undefined,
      proxy
    },
    build: {
      outDir: "dist",
      emptyOutDir: true,
      sourcemap: false,
      rollupOptions: {
        output: {
          entryFileNames: "app.js",
          chunkFileNames: "assets/[name].js",
          assetFileNames: "assets/[name][extname]"
        }
      }
    }
  };
});

export function nativeViteCacheDir(input: { nativeApiUrl: string; nativeCaseRunEnabled: boolean }): string {
  const runtime = input.nativeApiUrl ? "workbench" : input.nativeCaseRunEnabled ? "caserun" : "cloud-web";
  return `.state/vite/${runtime}`;
}

function nativeRuntimeConfigPlugin(serialized: string): Plugin {
  const config = parseWorkbenchNativeRuntimeConfig(serialized);
  const inlineJson = JSON.stringify(config).replace(/</gu, "\\u003c");
  return {
    name: "hwlab-workbench-native-runtime-config",
    transformIndexHtml() {
      return [{ tag: "script", injectTo: "head-prepend", children: `window.HWLAB_CLOUD_WEB_CONFIG=${inlineJson};` }];
    }
  };
}

function nativeCaseRunRuntimeConfigPlugin(): Plugin {
  return {
    name: "hwlab-caserun-native-runtime-config",
    configureServer(server) {
      server.middlewares.use((request, response, next) => {
        if (request.url !== "/v1/web-performance" || request.method !== "POST") return next();
        response.writeHead(204, { "cache-control": "no-store" });
        response.end();
      });
    },
    transformIndexHtml() {
      return [{
        tag: "script",
        injectTo: "head-prepend",
        children: `window.HWLAB_CLOUD_WEB_CONFIG = {
          displayTime: { timeZone: "Asia/Shanghai", locale: "zh-CN", label: "北京时间" },
          workbench: {
            realtimeFeatures: { liveKafkaSse: false, kafkaRefreshReplay: false, projectionRealtime: true },
            debugCapabilities: { isolatedKafka: false, rawHwlabEventWindow: { enabled: false, maxEntries: 1, maxRetainedBytes: 1 } },
            traceTimeline: { autoExpandRunning: false, autoCollapseTerminal: false },
            runtimePolicy: {}
          }
        };`
      }];
    }
  };
}

export function parseWorkbenchNativeRuntimeConfig(serialized: string): Record<string, unknown> {
  let value: unknown;
  try { value = JSON.parse(serialized); }
  catch { throw new Error("WORKBENCH_WEB_RUNTIME_CONFIG must be valid JSON"); }
  const config = record(value, "WORKBENCH_WEB_RUNTIME_CONFIG");
  const displayTime = record(config.displayTime, "WORKBENCH_WEB_RUNTIME_CONFIG.displayTime");
  requiredString(displayTime.timeZone, "displayTime.timeZone");
  requiredString(displayTime.locale, "displayTime.locale");
  requiredString(displayTime.label, "displayTime.label");
  const workbench = record(config.workbench, "WORKBENCH_WEB_RUNTIME_CONFIG.workbench");
  const realtime = record(workbench.realtimeFeatures, "workbench.realtimeFeatures");
  requiredBoolean(realtime.liveKafkaSse, "workbench.realtimeFeatures.liveKafkaSse");
  requiredBoolean(realtime.kafkaRefreshReplay, "workbench.realtimeFeatures.kafkaRefreshReplay");
  requiredBoolean(realtime.projectionRealtime, "workbench.realtimeFeatures.projectionRealtime");
  const debug = record(workbench.debugCapabilities, "workbench.debugCapabilities");
  requiredBoolean(debug.isolatedKafka, "workbench.debugCapabilities.isolatedKafka");
  const rawWindow = record(debug.rawHwlabEventWindow, "workbench.debugCapabilities.rawHwlabEventWindow");
  requiredBoolean(rawWindow.enabled, "workbench.debugCapabilities.rawHwlabEventWindow.enabled");
  requiredPositiveInteger(rawWindow.maxEntries, "workbench.debugCapabilities.rawHwlabEventWindow.maxEntries");
  requiredPositiveInteger(rawWindow.maxRetainedBytes, "workbench.debugCapabilities.rawHwlabEventWindow.maxRetainedBytes");
  const traceTimeline = record(workbench.traceTimeline, "workbench.traceTimeline");
  requiredBoolean(traceTimeline.autoExpandRunning, "workbench.traceTimeline.autoExpandRunning");
  requiredBoolean(traceTimeline.autoCollapseTerminal, "workbench.traceTimeline.autoCollapseTerminal");
  return config;
}

function record(value: unknown, path: string): Record<string, unknown> {
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`);
  return value as Record<string, unknown>;
}
function requiredString(value: unknown, path: string): void { if (typeof value !== "string" || !value.trim()) throw new Error(`${path} must be a non-empty string`); }
function requiredBoolean(value: unknown, path: string): void { if (typeof value !== "boolean") throw new Error(`${path} must be a boolean`); }
function requiredPositiveInteger(value: unknown, path: string): void { if (!Number.isInteger(value) || Number(value) <= 0) throw new Error(`${path} must be a positive integer`); }
