import { builtinModules } from "node:module";
import type { AddressInfo } from "node:net";

import type { ConfigEnv, Plugin, UserConfig } from "vite";

import pkg from "../package.json";

export const builtins = ["electron", ...builtinModules.map((m) => [m, `node:${m}`]).flat()];

// Filter out specified dependencies so they are not externalized for the main process
const dependenciesToBundle = [
  "electron-log",
  "node-schedule",
  "update-electron-app",
  "electron-updater",
  "electron-store",
  "axios",
  // "electron-squirrel-startup", // Essential for Windows packaging
];
export const external = [
  ...builtins,
  ...Object.keys("dependencies" in pkg ? (pkg.dependencies as Record<string, unknown>) : {}).filter(
    (dep) => !dependenciesToBundle.includes(dep),
  ),
];

export function getBuildConfig(env: ConfigEnv<"build">): UserConfig {
  const { root, mode, command } = env;

  return {
    root,
    mode,
    build: {
      // Prevent multiple builds from interfering with each other.
      emptyOutDir: false,
      // 🚧 Multiple builds may conflict.
      outDir: ".vite/build",
      watch: command === "serve" ? {} : null,
      minify: command === "build",
    },
    optimizeDeps: {
      include: [
        "wallpaper",
        "electron-log",
        "node-schedule",
        "electron-store",
        "axios",
        // "electron-squirrel-startup",
      ],
    },
    clearScreen: false,
  };
}

export function getDefineKeys(names: string[]) {
  const define: { [name: string]: VitePluginRuntimeKeys } = {};

  return names.reduce((acc, name) => {
    const NAME = name.toUpperCase();
    const keys: VitePluginRuntimeKeys = {
      VITE_DEV_SERVER_URL: `${NAME}_VITE_DEV_SERVER_URL`,
      VITE_NAME: `${NAME}_VITE_NAME`,
      VITE_SERVER_BASE_URL: `${NAME}_VITE_SERVER_BASE_URL`,
      VITE_SENTRY_DSN: `${NAME}_VITE_SENTRY_DSN`,
      VITE_PUBLIC_POSTHOG_KEY: `${NAME}_VITE_PUBLIC_POSTHOG_KEY`,
      VITE_PUBLIC_POSTHOG_HOST: `${NAME}_VITE_PUBLIC_POSTHOG_HOST`,
    };

    return { ...acc, [name]: keys };
  }, define);
}

export function getBuildDefine(env: ConfigEnv<"build">) {
  const { command, forgeConfig } = env;
  const names = forgeConfig.renderer.filter(({ name }) => name != null).map(({ name }) => name as string);
  const defineKeys = getDefineKeys(names);

  // Load environment variables
  const envVars = {
    // Use VITE_SERVER_BASE_URL from process.env for the main process
    "process.env.SERVER_BASE_URL": JSON.stringify(process.env.VITE_SERVER_BASE_URL),
    "process.env.SENTRY_DSN": JSON.stringify(process.env.SENTRY_DSN),
    // Add environment variables needed for the update system
    // Always set NODE_ENV to production for packaged builds
    "process.env.NODE_ENV": JSON.stringify("production"),
    "process.env.S3_BUCKET": JSON.stringify(process.env.S3_BUCKET),
    "process.env.S3_REGION": JSON.stringify(process.env.S3_REGION),
  };

  const define = Object.entries(defineKeys).reduce(
    (acc, [name, keys]) => {
      const { VITE_DEV_SERVER_URL, VITE_NAME, VITE_SERVER_BASE_URL, VITE_SENTRY_DSN } = keys;
      const def = {
        [VITE_DEV_SERVER_URL]: command === "serve" ? JSON.stringify(process.env[VITE_DEV_SERVER_URL]) : undefined,
        [VITE_NAME]: JSON.stringify(name),
        // Use VITE_SERVER_BASE_URL from process.env for the renderer's VITE_SERVER_BASE_URL variable
        [VITE_SERVER_BASE_URL]: JSON.stringify(process.env.VITE_SERVER_BASE_URL),
        [VITE_SENTRY_DSN]: JSON.stringify(process.env.SENTRY_DSN),
      };
      return { ...acc, ...def };
    },
    {} as Record<string, unknown>,
  );

  // Merge both objects so environment variables are available in both main and renderer processes
  return { ...define, ...envVars };
}

export function pluginExposeRenderer(name: string): Plugin {
  const { VITE_DEV_SERVER_URL } = getDefineKeys([name])[name];

  return {
    name: "@electron-forge/plugin-vite:expose-renderer",
    configureServer(server) {
      process.viteDevServers ??= {};
      // Expose server for preload scripts hot reload.
      process.viteDevServers[name] = server;

      server.httpServer?.once("listening", () => {
        const addressInfo = server.httpServer?.address() as AddressInfo;
        // Expose env constant for main process use.
        process.env[VITE_DEV_SERVER_URL] = `http://localhost:${addressInfo?.port}`;
      });
    },
  };
}

export function pluginHotRestart(command: "reload" | "restart"): Plugin {
  return {
    name: "@electron-forge/plugin-vite:hot-restart",
    closeBundle() {
      if (command === "reload") {
        for (const server of Object.values(process.viteDevServers)) {
          // Preload scripts hot reload.
          server.ws.send({ type: "full-reload" });
        }
      } else {
        // Main process hot restart.
        // https://github.com/electron/forge/blob/v7.2.0/packages/api/core/src/api/start.ts#L216-L223
        process.stdin.emit("data", "rs");
      }
    },
  };
}
