import { execSync } from "node:child_process";
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { dirname, resolve } from "node:path";

// --------------- types ---------------

export interface SystemdUnitOptions {
	/** Absolute path to the Node.js binary */
	nodePath: string;
	/** Absolute path to the daemon script (cli.js) */
	scriptPath: string;
	/** Absolute path to the config file */
	configPath: string;
}

export interface SystemdStatus {
	/** Whether the service is registered with systemd */
	registered: boolean;
	/** PID if currently running, null otherwise */
	pid: number | null;
	/** Last exit status code, null if never exited or not available */
	lastExitStatus: number | null;
}

export type RunCommandFn = (cmd: string) => string;

// --------------- constants ---------------

const SERVICE_NAME = "sf-server.service";

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

/** Return the canonical unit file path under ~/.config/systemd/user/. */
export function getServicePath(): string {
	const userDir = process.env["SF_SYSTEMD_USER_DIR"];
	return userDir
		? resolve(userDir, SERVICE_NAME)
		: resolve(homedir(), ".config", "systemd", "user", SERVICE_NAME);
}

// --------------- unit file generation ---------------

/** Generate a systemd user unit file for the SF server. */
export function generateUnit(opts: SystemdUnitOptions): string {
	const home = homedir();
	const stdoutPath = resolve(home, ".sf", "daemon-stdout.log");
	const stderrPath = resolve(home, ".sf", "daemon-stderr.log");

	return `[Unit]
Description=SF Server (singularity-forge operator entrypoint)
After=network.target

[Service]
Type=simple
ExecStart=${shellArg(opts.nodePath)} ${shellArg(opts.scriptPath)} --config ${shellArg(opts.configPath)}
Restart=on-failure
RestartSec=15
StandardOutput=append:${stdoutPath}
StandardError=append:${stderrPath}
Environment=HOME=${home}

[Install]
WantedBy=default.target
`;
}

// --------------- install / uninstall / status ---------------

/** Default runCommand using execSync. */
function defaultRunCommand(cmd: string): string {
	return execSync(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
}

/**
 * Install the systemd user unit: write the .service file and enable it.
 * Idempotent — stops + disables first if already loaded.
 */
export function install(
	opts: SystemdUnitOptions,
	runCommand: RunCommandFn = defaultRunCommand,
): void {
	const servicePath = getServicePath();
	const unit = generateUnit(opts);

	// Stop + disable first if already present (ignore errors)
	if (existsSync(servicePath)) {
		try {
			runCommand(`systemctl --user disable --now ${SERVICE_NAME}`);
		} catch {
			// not loaded — fine
		}
	}

	mkdirSync(dirname(servicePath), { recursive: true });
	writeFileSync(servicePath, unit, "utf-8");

	runCommand("systemctl --user daemon-reload");
	runCommand(`systemctl --user enable --now ${SERVICE_NAME}`);
}

/**
 * Uninstall the systemd user unit: disable + remove the .service file.
 * Graceful — does not throw if already uninstalled.
 */
export function uninstall(runCommand: RunCommandFn = defaultRunCommand): void {
	const servicePath = getServicePath();

	if (existsSync(servicePath)) {
		try {
			runCommand(`systemctl --user disable --now ${SERVICE_NAME}`);
		} catch {
			// already stopped/disabled — fine
		}
		unlinkSync(servicePath);
		try {
			runCommand("systemctl --user daemon-reload");
		} catch {
			// best-effort reload
		}
	}
	// If unit file doesn't exist, nothing to do — already uninstalled
}

/**
 * Query systemd for the service's status.
 * Returns structured information about registration, PID, and last exit code.
 *
 * Parses output from:
 *   systemctl --user show sf-server.service --property MainPID,ExecMainStatus,LoadState
 */
export function status(
	runCommand: RunCommandFn = defaultRunCommand,
): SystemdStatus {
	try {
		const output = runCommand(
			`systemctl --user show ${SERVICE_NAME} --property MainPID,ExecMainStatus,LoadState`,
		);

		// LoadState=not-found means the unit is not known to systemd
		const loadStateMatch = output.match(/^LoadState=(.+)$/m);
		const loadState = loadStateMatch ? loadStateMatch[1].trim() : null;
		if (loadState === "not-found" || loadState === null) {
			return { registered: false, pid: null, lastExitStatus: null };
		}

		const pidMatch = output.match(/^MainPID=(\d+)$/m);
		const exitMatch = output.match(/^ExecMainStatus=(\d+)$/m);

		const rawPid = pidMatch ? parseInt(pidMatch[1], 10) : null;
		// systemd reports MainPID=0 when not running
		const pid =
			rawPid === null || rawPid === 0 || Number.isNaN(rawPid) ? null : rawPid;

		const rawExit = exitMatch ? parseInt(exitMatch[1], 10) : null;
		const lastExitStatus =
			rawExit === null || Number.isNaN(rawExit) ? null : rawExit;

		return { registered: true, pid, lastExitStatus };
	} catch {
		// systemctl exits non-zero when the unit is completely unknown
		return { registered: false, pid: null, lastExitStatus: null };
	}
}

function shellArg(value: string): string {
	if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value;
	return `'${value.replaceAll("'", "'\\''")}'`;
}
