import path from "node:path";
import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";

import { defineConfig } from "vitest/config";
import { loadEnv, type Plugin } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const streamBrowserifyPath = require.resolve("stream-browserify");

function createIamApiAdapter(apiBaseUrl: string): Plugin {
  const upstreamBaseUrl = `${apiBaseUrl.replace(/\/+$/, "")}/api`;

  return {
    name: "iam-api-adapter",
    configureServer(server) {
      server.middlewares.use("/um-api", async (req, res) => {
        const requestPath = req.url ?? "/";
        const normalizedPath = requestPath.replace(/^\/+/, "");
        const targetUrl = new URL(normalizedPath, `${upstreamBaseUrl}/`);

        try {
          const headers = new Headers();
          for (const [key, value] of Object.entries(req.headers)) {
            if (!value || key.toLowerCase() === "host") {
              continue;
            }

            if (Array.isArray(value)) {
              for (const item of value) {
                headers.append(key, item);
              }
              continue;
            }

            headers.set(key, value);
          }

          const body =
            req.method === "GET" || req.method === "HEAD"
              ? undefined
              : await new Promise<Buffer>((resolve, reject) => {
                  const chunks: Buffer[] = [];
                  req.on("data", (chunk) =>
                    chunks.push(
                      Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk),
                    ),
                  );
                  req.on("end", () => resolve(Buffer.concat(chunks)));
                  req.on("error", reject);
                });

          const upstreamResponse = await fetch(targetUrl, {
            method: req.method,
            headers,
            body,
          });

          if (targetUrl.pathname.endsWith("/auth/me")) {
            const payload = await upstreamResponse.json();
            const unwrappedPayload =
              payload &&
              typeof payload === "object" &&
              "success" in payload &&
              "data" in payload
                ? payload.data
                : payload;

            res.statusCode = upstreamResponse.status;
            res.setHeader("content-type", "application/json; charset=utf-8");
            res.end(JSON.stringify(unwrappedPayload));
            return;
          }

          res.statusCode = upstreamResponse.status;
          upstreamResponse.headers.forEach((value, key) => {
            res.setHeader(key, value);
          });
          res.end(Buffer.from(await upstreamResponse.arrayBuffer()));
        } catch (error) {
          server.ssrFixStacktrace(error as Error);
          res.statusCode = 502;
          res.setHeader("content-type", "application/json; charset=utf-8");
          res.end(
            JSON.stringify({
              message: "Failed to forward IAM request",
            }),
          );
        }
      });
    },
  };
}

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, __dirname, "");
  const apiBaseUrl =
    env.VITE_BASE_API_URL?.trim() || "http://localhost:3000";

  return {
    plugins: [react(), tailwindcss(), createIamApiAdapter(apiBaseUrl)],
    resolve: {
      alias: {
        "@": path.resolve(__dirname, "./src"),
        "node:buffer": "buffer",
        "node:stream": streamBrowserifyPath,
        // Resolve from TS source so Vite gets ESM named exports (dist is CommonJS).
        "@edr/types": path.resolve(
          __dirname,
          "../../../packages/types/src/index.ts",
        ),
      },
      // Force a single copy of these singletons so MantineProvider context is
      // shared between the backoffice app and @edr/ui-common (which ships its
      // own node_modules copy). Without this, two separate @mantine/core
      // instances are bundled and the context lookup fails at runtime.
      dedupe: ["react", "react-dom", "@mantine/core", "@mantine/hooks"],
    },
    server: {
      port: 5183,
      host: "0.0.0.0",
    },
    test: {
      environment: "node",
    },
  };
});
