import { existsSync, readFileSync, statSync } from "node:fs";
import { homedir } from "node:os";
import { join, resolve } from "node:path";

export const SWARMS_REGISTRY_PATH = join(homedir(), ".sf", "swarms.json");
export const STATUS_PROJECTION_FILE = "status.projection.json";

export interface SwarmRegistryEntry {
	id?: string;
	name?: string;
	path?: string;
	repoPath?: string;
}

export interface SwarmStatusProjection {
	projectionVersion: number;
	swarmId: string;
	repoPath: string;
	repoName: string;
	writer: {
		surface: string;
		pid: number;
		writtenAt: string;
	};
	supervisor: {
		kind: string;
		health: string;
		currentPid: number | null;
	};
	state: {
		activeMilestoneId: string | null;
		activeMilestoneTitle: string | null;
		activeSliceId: string | null;
		activeSliceTitle: string | null;
		phase: string;
		nextAction: string;
		nextUnitType: string | null;
		nextUnitId: string | null;
		queueDepth: number;
		lastCycleOutcome: string | null;
		currentUnit: string | null;
	};
	health: {
		verdict: string | null;
		classification: string | null;
		issues: number;
	};
}

export interface SwarmDashboardRow {
	id: string;
	name: string;
	repoPath: string;
	projectionPath: string;
	status: "ok" | "missing" | "degraded";
	error: string | null;
	projection: SwarmStatusProjection | null;
	updatedAt: string | null;
}

function normalizeRegistry(raw: unknown): SwarmRegistryEntry[] {
	if (Array.isArray(raw)) return raw.filter(isRegistryEntry);
	if (raw && typeof raw === "object") {
		const entries = (raw as Record<string, unknown>).swarms;
		if (Array.isArray(entries)) return entries.filter(isRegistryEntry);
	}
	return [];
}

function isRegistryEntry(value: unknown): value is SwarmRegistryEntry {
	if (!value || typeof value !== "object") return false;
	const entry = value as Record<string, unknown>;
	return typeof entry.path === "string" || typeof entry.repoPath === "string";
}

function readRegistry(
	registryPath = SWARMS_REGISTRY_PATH,
): SwarmRegistryEntry[] {
	if (!existsSync(registryPath)) return [];
	const parsed = JSON.parse(readFileSync(registryPath, "utf-8"));
	return normalizeRegistry(parsed);
}

function validateProjection(value: unknown): SwarmStatusProjection {
	if (!value || typeof value !== "object") {
		throw new Error("projection is not an object");
	}
	const projection = value as SwarmStatusProjection;
	if (projection.projectionVersion !== 1) {
		throw new Error(
			`unsupported projectionVersion ${projection.projectionVersion}`,
		);
	}
	if (
		typeof projection.repoPath !== "string" ||
		projection.repoPath.length === 0
	) {
		throw new Error("projection repoPath missing");
	}
	if (!projection.state || typeof projection.state !== "object") {
		throw new Error("projection state missing");
	}
	return projection;
}

export function readSwarmDashboardRows(
	registryPath = SWARMS_REGISTRY_PATH,
): SwarmDashboardRow[] {
	const entries = readRegistry(registryPath);
	return entries.map((entry, index) => {
		const repoPath = resolve(entry.repoPath ?? entry.path ?? "");
		const projectionPath = join(repoPath, ".sf", STATUS_PROJECTION_FILE);
		const fallbackName =
			repoPath.split(/[\\/]/).filter(Boolean).at(-1) ?? repoPath;
		const id = entry.id ?? repoPath;
		const name = entry.name ?? fallbackName;
		if (!existsSync(projectionPath)) {
			return {
				id,
				name,
				repoPath,
				projectionPath,
				status: "missing",
				error: "status projection missing",
				projection: null,
				updatedAt: null,
			};
		}
		try {
			const projection = validateProjection(
				JSON.parse(readFileSync(projectionPath, "utf-8")),
			);
			return {
				id: projection.swarmId || id || `swarm-${index + 1}`,
				name: entry.name ?? projection.repoName ?? name,
				repoPath,
				projectionPath,
				status: "ok",
				error: null,
				projection,
				updatedAt: statSync(projectionPath).mtime.toISOString(),
			};
		} catch (error) {
			return {
				id,
				name,
				repoPath,
				projectionPath,
				status: "degraded",
				error: error instanceof Error ? error.message : String(error),
				projection: null,
				updatedAt: null,
			};
		}
	});
}
