import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, test } from "vitest";

const cliWeb = await import("../../cli-web-branch.ts");
const webMode = await import("../../web-mode.ts");

// ─── CLI flag parsing ────────────────────────────────────────────────

test("parseCliArgs captures --host flag", () => {
	const flags = cliWeb.parseCliArgs([
		"node",
		"dist/loader.js",
		"server",
		"--host",
		"0.0.0.0",
	]);
	assert.deepEqual(flags.messages, ["server"]);
	assert.equal(flags.webHost, "0.0.0.0");
});

test("parseCliArgs captures --port flag", () => {
	const flags = cliWeb.parseCliArgs([
		"node",
		"dist/loader.js",
		"server",
		"--port",
		"8080",
	]);
	assert.deepEqual(flags.messages, ["server"]);
	assert.equal(flags.webPort, 8080);
});

test("parseCliArgs ignores invalid port values", () => {
	const flags = cliWeb.parseCliArgs([
		"node",
		"dist/loader.js",
		"server",
		"--port",
		"abc",
	]);
	assert.equal(flags.webPort, undefined);
});

test("parseCliArgs ignores out-of-range port", () => {
	const flags = cliWeb.parseCliArgs([
		"node",
		"dist/loader.js",
		"server",
		"--port",
		"99999",
	]);
	assert.equal(flags.webPort, undefined);
});

test("parseCliArgs captures --allowed-origins flag", () => {
	const flags = cliWeb.parseCliArgs([
		"node",
		"dist/loader.js",
		"server",
		"--allowed-origins",
		"http://192.168.1.10:3000",
	]);
	assert.deepEqual(flags.webAllowedOrigins, ["http://192.168.1.10:3000"]);
});

test("parseCliArgs splits comma-separated allowed origins", () => {
	const flags = cliWeb.parseCliArgs([
		"node",
		"dist/loader.js",
		"server",
		"--allowed-origins",
		"http://192.168.1.10:3000,http://tailscale-host:3000",
	]);
	assert.deepEqual(flags.webAllowedOrigins, [
		"http://192.168.1.10:3000",
		"http://tailscale-host:3000",
	]);
});

test("parseCliArgs captures all web network flags together", () => {
	const flags = cliWeb.parseCliArgs([
		"node",
		"dist/loader.js",
		"server",
		"--host",
		"0.0.0.0",
		"--port",
		"4000",
		"--allowed-origins",
		"http://my-tailscale:4000",
	]);
	assert.equal(flags.webHost, "0.0.0.0");
	assert.equal(flags.webPort, 4000);
	assert.deepEqual(flags.webAllowedOrigins, ["http://my-tailscale:4000"]);
});

test("parseCliArgs does not set network flags when not provided", () => {
	const flags = cliWeb.parseCliArgs(["node", "dist/loader.js", "server"]);
	assert.equal(flags.webHost, undefined);
	assert.equal(flags.webPort, undefined);
	assert.equal(flags.webAllowedOrigins, undefined);
});

// ─── launchWebMode env forwarding ────────────────────────────────────

test("launchWebMode forwards custom host, port, and allowed origins to subprocess env", async (_t) => {
	const tmp = mkdtempSync(join(tmpdir(), "sf-web-net-"));
	const standaloneRoot = join(tmp, "dist", "web", "standalone");
	const serverPath = join(standaloneRoot, "server.js");
	mkdirSync(standaloneRoot, { recursive: true });
	writeFileSync(serverPath, 'console.log("stub")\n');

	let spawnEnv: Record<string, string> | undefined;

	afterEach(() => {
		rmSync(tmp, { recursive: true, force: true });
	});

	const status = await webMode.launchWebMode(
		{
			cwd: "/tmp/project",
			projectSessionsDir: "/tmp/.sf/sessions",
			agentDir: "/tmp/.sf/agent",
			packageRoot: tmp,
			host: "0.0.0.0",
			port: 8080,
			allowedOrigins: [
				"http://192.168.1.10:8080",
				"http://tailscale-host:8080",
			],
		},
		{
			initResources: () => {},
			spawn: (_command, _args, options) => {
				spawnEnv = (options as { env: Record<string, string> }).env;
				return { pid: 99999, once: () => undefined, unref: () => {} } as any;
			},
			waitForBootReady: async () => undefined,
			openBrowser: () => {},
			stderr: { write: () => true },
		},
	);

	assert.equal(status.ok, true);
	if (!status.ok) throw new Error("expected success");
	assert.equal(status.host, "0.0.0.0");
	assert.equal(status.port, 8080);
	assert.equal(status.url, "http://0.0.0.0:8080");

	assert.ok(spawnEnv);
	assert.equal(spawnEnv!.HOSTNAME, "0.0.0.0");
	assert.equal(spawnEnv!.PORT, "8080");
	assert.equal(spawnEnv!.SF_WEB_HOST, "0.0.0.0");
	assert.equal(spawnEnv!.SF_WEB_PORT, "8080");
	assert.equal(
		spawnEnv!.SF_WEB_ALLOWED_ORIGINS,
		"http://192.168.1.10:8080,http://tailscale-host:8080",
	);
});

test("launchWebMode omits SF_WEB_ALLOWED_ORIGINS when none provided", async (_t) => {
	const tmp = mkdtempSync(join(tmpdir(), "sf-web-no-origins-"));
	const standaloneRoot = join(tmp, "dist", "web", "standalone");
	const serverPath = join(standaloneRoot, "server.js");
	mkdirSync(standaloneRoot, { recursive: true });
	writeFileSync(serverPath, 'console.log("stub")\n');

	let spawnEnv: Record<string, string> | undefined;

	afterEach(() => {
		rmSync(tmp, { recursive: true, force: true });
	});

	await webMode.launchWebMode(
		{
			cwd: "/tmp/project",
			projectSessionsDir: "/tmp/.sf/sessions",
			agentDir: "/tmp/.sf/agent",
			packageRoot: tmp,
		},
		{
			initResources: () => {},
			resolvePort: async () => 45000,
			env: { CLEAN_ENV: "1" },
			spawn: (_command, _args, options) => {
				spawnEnv = (options as { env: Record<string, string> }).env;
				return { pid: 99999, once: () => undefined, unref: () => {} } as any;
			},
			waitForBootReady: async () => undefined,
			openBrowser: () => {},
			stderr: { write: () => true },
		},
	);

	assert.ok(spawnEnv);
	assert.equal(spawnEnv!.SF_WEB_ALLOWED_ORIGINS, undefined);
});

// ─── runWebCliBranch end-to-end forwarding ───────────────────────────

test("runWebCliBranch forwards --host, --port, --allowed-origins to launchWebMode", async (_t) => {
	const tmp = mkdtempSync(join(tmpdir(), "sf-web-branch-flags-"));
	const projectDir = join(tmp, "project");
	mkdirSync(projectDir, { recursive: true });

	let receivedOptions: Record<string, unknown> | undefined;

	afterEach(() => {
		rmSync(tmp, { recursive: true, force: true });
	});

	const flags = cliWeb.parseCliArgs([
		"node",
		"dist/loader.js",
		"server",
		projectDir,
		"--host",
		"0.0.0.0",
		"--port",
		"9000",
		"--allowed-origins",
		"http://my-host:9000",
	]);

	const result = await cliWeb.runWebCliBranch(flags, {
		runWebMode: async (options) => {
			receivedOptions = options as unknown as Record<string, unknown>;
			return {
				mode: "web" as const,
				ok: true as const,
				cwd: options.cwd,
				projectSessionsDir: options.projectSessionsDir,
				host: "0.0.0.0",
				port: 9000,
				url: "http://0.0.0.0:9000",
				hostKind: "source-dev" as const,
				hostPath: "/tmp/fake-web/package.json",
				hostRoot: "/tmp/fake-web",
			};
		},
		stderr: { write: () => true },
	});

	assert.equal(result.handled, true);
	if (!result.handled) throw new Error("expected handled");
	assert.equal(result.exitCode, 0);
	assert.ok(receivedOptions);
	assert.equal(receivedOptions!.host, "0.0.0.0");
	assert.equal(receivedOptions!.port, 9000);
	assert.deepEqual(receivedOptions!.allowedOrigins, ["http://my-host:9000"]);
});
