import { spawn } from "node:child_process";
import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";

function requireCondition(value) {
  if (!value) throw new Error("Windows configuration migration smoke failed.");
}

function runSetup(executable, request, env) {
  return new Promise((resolveRun, rejectRun) => {
    const child = spawn(executable, ["--migrate-config"], {
      env,
      windowsHide: true,
      stdio: ["pipe", "ignore", "ignore"]
    });
    child.once("error", rejectRun);
    child.once("exit", (code) => code === 0
      ? resolveRun()
      : rejectRun(new Error("Windows configuration migration smoke failed.")));
    child.stdin.end(JSON.stringify(request));
  });
}

export async function smokeWindowsConfigMigration({ layout }) {
  const installedLayout = resolve(layout);
  const temporaryRoot = await mkdtemp(join(tmpdir(), "crm-windows-config-smoke-"));
  try {
    const configPath = join(temporaryRoot, "config.local.json");
    const dataDir = join(temporaryRoot, "data");
    const projectsRoot = join(temporaryRoot, "existing-projects");
    const existingProject = join(projectsRoot, "existing");
    const selectedProject = join(temporaryRoot, "selected-project");
    await Promise.all([
      mkdir(dataDir, { recursive: true }),
      mkdir(existingProject, { recursive: true }),
      mkdir(selectedProject, { recursive: true })
    ]);
    await writeFile(configPath, JSON.stringify({
      codexCliPath: "old-codex.cmd",
      engine: "fake",
      bindHost: "127.0.0.1",
      port: 43110,
      allowedOrigins: ["https://previous.example.ts.net"],
      dataDir,
      projectsRoot,
      accounts: [
        { id: "managed", label: "Managed", codexHome: join(temporaryRoot, "managed-home") },
        { id: "history", label: "History", codexHome: join(temporaryRoot, "history-home"), historyOnly: true }
      ],
      workspaces: [
        { id: "existing", label: "Existing", path: existingProject },
        { id: "missing", label: "Missing", path: join(projectsRoot, "missing") }
      ],
      futureSetting: { preserve: true }
    }, null, 2));

    await runSetup(join(installedLayout, "CodeRemoteManager.exe"), {
      codexCliPath: "current-codex.cmd",
      tailnetOrigin: "https://current.example.ts.net",
      projectPath: selectedProject,
      dataDir,
      desktopCodexHome: join(temporaryRoot, "desktop-home")
    }, {
      ...process.env,
      CODE_REMOTE_MANAGER_CONFIG_PATH: configPath,
      LOCALAPPDATA: temporaryRoot
    });

    const validatorModule = await import(pathToFileURL(
      join(installedLayout, "app", "backend", "src", "config.js")).href);
    const { config } = await validatorModule.loadConfigFromPath(configPath);
    requireCondition(config.schemaVersion === 1);
    requireCondition(config.codexCliPath === "current-codex.cmd");
    requireCondition(config.projectsRoot === projectsRoot);
    requireCondition(config.accounts.length === 2);
    requireCondition(config.accounts.some((account) => account.id === "history" && account.historyOnly === true));
    requireCondition(config.workspaces.length === 3);
    requireCondition(config.workspaces.some((workspace) => workspace.id === "missing"));
    requireCondition(config.workspaces.some((workspace) => workspace.path === selectedProject));
    requireCondition(config.futureSetting?.preserve === true);
    requireCondition(config.allowedOrigins.includes("https://previous.example.ts.net"));
    requireCondition(config.allowedOrigins.includes("https://current.example.ts.net"));

    const backupDirectory = join(temporaryRoot, "config-backups");
    const backupFiles = (await readdir(backupDirectory)).filter((name) => name.startsWith("config.local.") && name.endsWith(".json"));
    requireCondition(backupFiles.length === 1);
    const backup = JSON.parse(await readFile(join(backupDirectory, backupFiles[0]), "utf8"));
    requireCondition(backup.accounts.length === 2);
    requireCondition(backup.workspaces.length === 2);
    requireCondition(backup.futureSetting?.preserve === true);
  } finally {
    await rm(temporaryRoot, { recursive: true, force: true });
  }
}

const invokedPath = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).href : "";
if (invokedPath === import.meta.url) {
  const [layout] = process.argv.slice(2);
  if (!layout) {
    console.error("Windows configuration migration smoke failed.");
    process.exitCode = 1;
  } else {
    try {
      await smokeWindowsConfigMigration({ layout });
    } catch {
      console.error("Windows configuration migration smoke failed.");
      process.exitCode = 1;
    }
  }
}
