import assert from "node:assert/strict";
import {
	existsSync,
	mkdtempSync,
	rmSync,
	writeFileSync,
} from "node:fs";
import { homedir, tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { afterEach, describe, it } from "vitest";
import type { RunCommandFn, SystemdUnitOptions } from "./systemd.js";
import {
	generateUnit,
	getServicePath,
	install,
	status,
	uninstall,
} from "./systemd.js";

// ---------- helpers ----------

const cleanupDirs: string[] = [];
afterEach(() => {
	delete process.env["SF_SYSTEMD_USER_DIR"];
	while (cleanupDirs.length) {
		const d = cleanupDirs.pop()!;
		if (existsSync(d)) rmSync(d, { recursive: true, force: true });
	}
});

function fakeSystemdDir(): string {
	const dir = mkdtempSync(join(tmpdir(), "systemd-user-"));
	cleanupDirs.push(dir);
	process.env["SF_SYSTEMD_USER_DIR"] = dir;
	return dir;
}

function baseUnitOpts(
	overrides?: Partial<SystemdUnitOptions>,
): SystemdUnitOptions {
	return {
		nodePath: "/usr/local/bin/node",
		scriptPath: "/usr/local/lib/sf-daemon/dist/cli.js",
		configPath: join(homedir(), ".sf", "daemon.yaml"),
		...overrides,
	};
}

// ---------- generateUnit ----------

describe("generateUnit", () => {
	it("includes [Unit], [Service], and [Install] sections", () => {
		const unit = generateUnit(baseUnitOpts());
		assert.ok(unit.includes("[Unit]"));
		assert.ok(unit.includes("[Service]"));
		assert.ok(unit.includes("[Install]"));
	});

	it("sets ExecStart to nodePath scriptPath --config configPath", () => {
		const opts = baseUnitOpts({
			nodePath: "/home/user/.nvm/versions/node/v26.1.0/bin/node",
			scriptPath: "/usr/local/lib/sf/dist/cli.js",
			configPath: "/home/user/.sf/daemon.yaml",
		});
		const unit = generateUnit(opts);
		assert.ok(
			unit.includes(
				"ExecStart=/home/user/.nvm/versions/node/v26.1.0/bin/node /usr/local/lib/sf/dist/cli.js --config /home/user/.sf/daemon.yaml",
			),
		);
	});

	it("sets Restart=on-failure", () => {
		const unit = generateUnit(baseUnitOpts());
		assert.ok(unit.includes("Restart=on-failure"));
	});

	it("sets RestartSec=15", () => {
		const unit = generateUnit(baseUnitOpts());
		assert.ok(unit.includes("RestartSec=15"));
	});

	it("sets WantedBy=default.target", () => {
		const unit = generateUnit(baseUnitOpts());
		assert.ok(unit.includes("WantedBy=default.target"));
	});

	it("sets HOME environment variable", () => {
		const unit = generateUnit(baseUnitOpts());
		assert.ok(unit.includes(`Environment=HOME=${homedir()}`));
	});

	it("includes stdout and stderr log paths", () => {
		const unit = generateUnit(baseUnitOpts());
		assert.ok(unit.includes("daemon-stdout.log"));
		assert.ok(unit.includes("daemon-stderr.log"));
	});
});

// ---------- getServicePath ----------

describe("getServicePath", () => {
	it("returns ~/.config/systemd/user/sf-server.service", () => {
		const expected = resolve(
			homedir(),
			".config",
			"systemd",
			"user",
			"sf-server.service",
		);
		assert.equal(getServicePath(), expected);
	});

	it("honors SF_SYSTEMD_USER_DIR for tests and sandboxed installs", () => {
		const dir = fakeSystemdDir();

		assert.equal(getServicePath(), join(dir, "sf-server.service"));
	});
});

// ---------- install ----------

describe("install", () => {
	it("calls daemon-reload and enable --now after writing unit file", () => {
		fakeSystemdDir();
		const calls: string[] = [];
		const mockRun: RunCommandFn = (cmd: string) => {
			calls.push(cmd);
			return "";
		};

		install(baseUnitOpts(), mockRun);

		const reloadCalls = calls.filter((c) => c.includes("daemon-reload"));
		const enableCalls = calls.filter((c) => c.includes("enable --now"));
		assert.equal(reloadCalls.length, 1);
		assert.equal(enableCalls.length, 1);
	});

	it("writes correct unit file content", () => {
		// Test that generateUnit produces the right content (install is a thin wrapper)
		const unit = generateUnit(baseUnitOpts());
		assert.ok(unit.includes("[Service]"));
		assert.ok(unit.includes("Restart=on-failure"));
		assert.ok(unit.includes("WantedBy=default.target"));
	});

	it("disables existing unit before reinstalling", () => {
		const tmpDir = fakeSystemdDir();
		writeFileSync(join(tmpDir, "sf-server.service"), "[Service]\n", "utf-8");

		const calls: string[] = [];
		const mockRun: RunCommandFn = (cmd: string) => {
			calls.push(cmd);
			return "";
		};

		install(baseUnitOpts(), mockRun);

		assert.ok(calls.some((c) => c.includes("disable --now sf-server.service")));
		assert.ok(calls.some((c) => c.includes("daemon-reload")));
		assert.ok(calls.some((c) => c.includes("enable --now sf-server.service")));
	});
});

// ---------- uninstall ----------

describe("uninstall", () => {
	it("handles missing unit file gracefully (no-op)", () => {
		fakeSystemdDir();
		const calls: string[] = [];
		const mockRun: RunCommandFn = (cmd: string) => {
			calls.push(cmd);
			return "";
		};

		// If the service file doesn't exist, uninstall is a no-op
		assert.doesNotThrow(() => uninstall(mockRun));
	});

	it("handles already-stopped service gracefully", () => {
		const dir = fakeSystemdDir();
		writeFileSync(join(dir, "sf-server.service"), "[Service]\n", "utf-8");
		const mockRun: RunCommandFn = (cmd: string) => {
			if (cmd.includes("disable --now")) {
				throw new Error("Failed to stop sf-server.service: Unit not loaded.");
			}
			return "";
		};

		// Should not throw even if disable --now fails
		assert.doesNotThrow(() => uninstall(mockRun));
	});

	it("calls daemon-reload after removing unit file (when file exists)", () => {
		const dir = fakeSystemdDir();
		writeFileSync(join(dir, "sf-server.service"), "[Service]\n", "utf-8");
		const calls: string[] = [];
		const mockRun: RunCommandFn = (cmd: string) => {
			calls.push(cmd);
			return "";
		};

		uninstall(mockRun);
		assert.ok(calls.some((c) => c.includes("disable --now sf-server.service")));
		assert.ok(calls.some((c) => c.includes("daemon-reload")));
	});
});

// ---------- status ----------

describe("status", () => {
	it("returns registered=true with PID when service is running", () => {
		const mockRun: RunCommandFn = (_cmd: string) => {
			return "LoadState=loaded\nMainPID=4567\nExecMainStatus=0\n";
		};

		const result = status(mockRun);
		assert.equal(result.registered, true);
		assert.equal(result.pid, 4567);
		assert.equal(result.lastExitStatus, 0);
	});

	it("returns pid=null when MainPID=0 (stopped service)", () => {
		const mockRun: RunCommandFn = (_cmd: string) => {
			return "LoadState=loaded\nMainPID=0\nExecMainStatus=1\n";
		};

		const result = status(mockRun);
		assert.equal(result.registered, true);
		assert.equal(result.pid, null);
		assert.equal(result.lastExitStatus, 1);
	});

	it("returns registered=false when LoadState=not-found", () => {
		const mockRun: RunCommandFn = (_cmd: string) => {
			return "LoadState=not-found\nMainPID=0\nExecMainStatus=0\n";
		};

		const result = status(mockRun);
		assert.equal(result.registered, false);
		assert.equal(result.pid, null);
		assert.equal(result.lastExitStatus, null);
	});

	it("returns registered=false when systemctl exits non-zero", () => {
		const mockRun: RunCommandFn = (_cmd: string) => {
			throw new Error("Failed to connect to bus: No such file or directory");
		};

		const result = status(mockRun);
		assert.equal(result.registered, false);
		assert.equal(result.pid, null);
		assert.equal(result.lastExitStatus, null);
	});

	it("returns structured result with all fields", () => {
		const mockRun: RunCommandFn = (_cmd: string) => {
			return "LoadState=loaded\nMainPID=1234\nExecMainStatus=0\n";
		};

		const result = status(mockRun);
		assert.ok("registered" in result);
		assert.ok("pid" in result);
		assert.ok("lastExitStatus" in result);
	});

	it("handles unexpected output format gracefully (loaded but no PID line)", () => {
		const mockRun: RunCommandFn = (_cmd: string) => {
			return "LoadState=loaded\n";
		};

		// Should not throw — loaded unit with no PID/exit info
		const result = status(mockRun);
		assert.equal(result.registered, true);
		assert.equal(result.pid, null);
		assert.equal(result.lastExitStatus, null);
	});

	it("passes the correct systemctl command", () => {
		const cmds: string[] = [];
		const mockRun: RunCommandFn = (cmd: string) => {
			cmds.push(cmd);
			return "LoadState=loaded\nMainPID=0\nExecMainStatus=0\n";
		};

		status(mockRun);
		assert.equal(cmds.length, 1);
		assert.ok(cmds[0].includes("systemctl --user show sf-server.service"));
		assert.ok(cmds[0].includes("MainPID"));
		assert.ok(cmds[0].includes("ExecMainStatus"));
		assert.ok(cmds[0].includes("LoadState"));
	});
});
