#!/usr/bin/env node
const { spawn } = require("child_process");
const fs = require("fs");
const path = require("path");
const { pathToFileURL } = require("url");
const { createRequire } = require("module");
const { createTempFlagAssets } = require("./flag-assets.js");

let child = null;
let cleanupTemp = null;
let shuttingDown = false;

function resolveViteCli(cwd) {
  let vpkg = "";
  try {
    const projectRequire = createRequire(path.join(cwd, "package.json"));
    vpkg = projectRequire.resolve("vite/package.json");
  } catch {
    // Fall back to node_modules for non-PnP projects.
  }

  if (!vpkg) {
    vpkg = path.join(cwd, "node_modules", "vite", "package.json");
  }

  if (fs.existsSync(vpkg)) {
    const p = require(vpkg);
    const bin = typeof p.bin === "string" ? p.bin : p.bin && p.bin.vite;
    if (bin) {
      const cli = path.join(path.dirname(vpkg), bin);
      if (fs.existsSync(cli)) return cli;
    }
  }
  throw new Error(
    "未在当前项目找到 Vite（请先安装依赖；宿主项目可执行 yarn install 后再运行 mvframe-d）",
  );
}

function resolvePackageManager(cwd) {
  const pkgPath = path.join(cwd, "package.json");
  if (fs.existsSync(pkgPath)) {
    const pkg = require(pkgPath);
    const packageManager = String(pkg.packageManager || "");
    if (packageManager.startsWith("pnpm@")) return "pnpm";
    if (packageManager.startsWith("npm@")) return "npx";
    return "yarn";
  }
  return "yarn";
}

function findHostViteConfig(cwd) {
  const names = [
    "vite.config.ts",
    "vite.config.mts",
    "vite.config.js",
    "vite.config.mjs",
    "vite.config.cjs",
    "vite.config.cts",
  ];
  return names.map((name) => path.join(cwd, name)).find(fs.existsSync) || "";
}

function createWrapperConfig({ cwd, flagsRoot, tmpRoot }) {
  const hostConfig = findHostViteConfig(cwd);
  const hostImport = hostConfig
    ? `import hostConfig from ${JSON.stringify(pathToFileURL(hostConfig).href)};`
    : "const hostConfig = {};";
  const source = `
import fs from "node:fs";
import path from "node:path";
${hostImport}

function createFlagAssetsPlugin(flagsRoot) {
  return {
    name: "mvframe-host-flag-assets",
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        const pathname = req.url?.split("?")[0] || "";
        if (!pathname.startsWith("/assets/flags/")) {
          next();
          return;
        }

        const relativePath = decodeURIComponent(
          pathname.replace(/^\\/assets\\/flags\\//, ""),
        );
        const normalized = path.posix.normalize(relativePath);
        if (
          normalized.startsWith("../") ||
          !/^(1x1|4x3)\\/[a-z0-9-]+\\.svg$/.test(normalized)
        ) {
          res.statusCode = 404;
          res.end();
          return;
        }

        const filePath = path.join(flagsRoot, normalized);
        fs.readFile(filePath, (err, content) => {
          if (err) {
            res.statusCode = 404;
            res.end();
            return;
          }
          res.setHeader("Content-Type", "image/svg+xml");
          res.end(content);
        });
      });
    },
  };
}

function toArray(value) {
  if (!value) return [];
  return Array.isArray(value) ? value : [value];
}

export default async function mvframeWrappedConfig(configEnv) {
  const loaded =
    typeof hostConfig === "function" ? await hostConfig(configEnv) : hostConfig;
  const base = loaded || {};
  const server = { ...(base.server || {}) };
  if (server.host == null) {
    server.host = "0.0.0.0";
  }

  return {
    ...base,
    root: base.root || ${JSON.stringify(cwd)},
    server,
    plugins: [
      ...toArray(base.plugins),
      createFlagAssetsPlugin(${JSON.stringify(flagsRoot)}),
    ],
  };
}
`;
  const configPath = path.join(tmpRoot, "mvframe-vite.config.mjs");
  fs.writeFileSync(configPath, source, "utf8");
  return configPath;
}

function shutdown(code = 0) {
  if (shuttingDown) return;
  shuttingDown = true;
  if (child && !child.killed) {
    child.kill("SIGTERM");
  }
  if (cleanupTemp) {
    cleanupTemp();
  }
  setTimeout(() => process.exit(code), 200);
}

process.on("SIGINT", () => shutdown(0));
process.on("SIGTERM", () => shutdown(0));

function main() {
  const cwd = process.cwd();
  const viteEntry = resolveViteCli(cwd);
  const tempFlags = createTempFlagAssets();
  cleanupTemp = tempFlags.cleanup;
  const configPath = createWrapperConfig({
    cwd,
    flagsRoot: tempFlags.flagsRoot,
    tmpRoot: tempFlags.tmpRoot,
  });

  child = spawn(
    viteEntry.includes(".zip/") ? resolvePackageManager(cwd) : process.execPath,
    viteEntry.includes(".zip/")
      ? ["vite", "--host", "0.0.0.0", "--config", configPath]
      : [viteEntry, "--host", "0.0.0.0", "--config", configPath],
    {
      cwd,
      env: process.env,
      stdio: "inherit",
      shell: false,
    },
  );

  child.on("exit", (code, signal) => {
    if (shuttingDown) return;
    if (cleanupTemp) {
      cleanupTemp();
    }
    process.exit(code === null ? (signal ? 1 : 0) : code);
  });

  child.on("error", (error) => {
    console.error(`[mvframe-d] failed to start vite: ${error.message}`);
    shutdown(1);
  });
}

try {
  main();
} catch (error) {
  console.error(`[mvframe-d] failed to start vite: ${error.message}`);
  process.exit(1);
}
