import test from "node:test";
import assert from "node:assert/strict";
import { access, cp, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { spawn } from "node:child_process";
import { stageWindowsBackendRuntime } from "./stage-windows-backend-runtime.mjs";
import { smokeWindowsInstalledLayout } from "./smoke-windows-installed-layout.mjs";

const root = fileURLToPath(new URL("../", import.meta.url));

async function temporaryTestDirectory(t) {
  const directory = await mkdtemp(join(tmpdir(), "crm-windows-layout-test-"));
  t.after(() => rm(directory, { recursive: true, force: true }));
  return directory;
}

async function runNode(args, options) {
  const child = spawn(process.execPath, args, {
    ...options,
    windowsHide: true,
    stdio: ["ignore", "pipe", "pipe"]
  });
  let stderr = "";
  child.stderr.on("data", (chunk) => { stderr += String(chunk); });
  const code = await new Promise((resolve, reject) => {
    child.once("error", reject);
    child.once("exit", resolve);
  });
  return { code, stderr };
}

test("staged Windows Backend resolves production dependencies outside the repository", async (t) => {
  const directory = await temporaryTestDirectory(t);
  const backend = join(directory, "layout", "app", "backend");
  const packages = await stageWindowsBackendRuntime({ root, target: backend });

  assert.deepEqual(packages, ["entities@6.0.1", "parse5@8.0.0"]);
  await access(join(backend, "src", "index.js"));
  await access(join(backend, "src", "device-pairing-cli.js"));
  await access(join(directory, "layout", "packages", "shared", "src", "device-pairing.js"));
  await access(join(backend, "node_modules", "parse5", "package.json"));
  await access(join(backend, "node_modules", "entities", "package.json"));
  await assert.rejects(access(join(backend, "src", "workspace-artifacts.test.js")));

  const artifactModule = pathToFileURL(join(backend, "src", "workspace-artifacts.js")).href;
  const result = await runNode(["--input-type=module", "--eval", `await import(${JSON.stringify(artifactModule)})`], {
    cwd: directory
  });
  assert.equal(result.code, 0, result.stderr);
});

test("staged Windows Backend startup rejects a non-loopback bind host", async (t) => {
  const directory = await temporaryTestDirectory(t);
  const backend = join(directory, "layout", "app", "backend");
  const configPath = join(directory, "unsafe-config.json");
  await stageWindowsBackendRuntime({ root, target: backend });
  await writeFile(configPath, JSON.stringify({
    codexCliPath: "codex.cmd",
    engine: "fake",
    bindHost: "0.0.0.0",
    port: 43110,
    dataDir: join(directory, "data"),
    accounts: [],
    workspaces: [{ id: "smoke", label: "Smoke", path: directory }]
  }));

  const result = await runNode([join(backend, "src", "index.js")], {
    cwd: directory,
    env: { ...process.env, CODE_REMOTE_MANAGER_CONFIG_PATH: configPath }
  });

  assert.notEqual(result.code, 0);
  assert.match(result.stderr, /configuration_bind_host_not_loopback/);
});

test("staged Windows host reaches Backend health without repository node_modules", async (t) => {
  const directory = await temporaryTestDirectory(t);
  const layout = join(directory, "layout");
  await stageWindowsBackendRuntime({ root, target: join(layout, "app", "backend") });
  await mkdir(join(layout, "host"), { recursive: true });
  await mkdir(join(layout, "app", "pwa"), { recursive: true });
  await cp(join(root, "windows-host", "host.mjs"), join(layout, "host", "host.mjs"));
  await cp(join(root, "windows-host", "host-lib.mjs"), join(layout, "host", "host-lib.mjs"));
  await writeFile(join(layout, "app", "pwa", "index.html"), "installed-layout-smoke");

  await smokeWindowsInstalledLayout({ layout, nodePath: process.execPath, timeoutMs: 15_000 });
});
