import * as fs from "node:fs";
import * as path from "node:path";
import type { TeamRunManifest } from "../state/types.ts";
import { DEFAULT_PATHS } from "../config/defaults.ts";
import { findRepoRoot, projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
import { activeRunEntries } from "../state/active-run-registry.ts";
import { isSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";

function readManifest(filePath: string): TeamRunManifest | undefined {
	try {
		return JSON.parse(fs.readFileSync(filePath, "utf-8")) as TeamRunManifest;
	} catch {
		return undefined;
	}
}

function collectRuns(root: string, maxEntries?: number): TeamRunManifest[] {
	const runsRoot = path.join(root, DEFAULT_PATHS.state.runsSubdir);
	if (!fs.existsSync(runsRoot)) return [];
	const entries = fs.readdirSync(runsRoot, { withFileTypes: true })
		.filter((entry) => entry.isDirectory() && isSafePathId(entry.name))
		.map((entry) => entry.name)
		.sort((a, b) => b.localeCompare(a));
	const selected = maxEntries !== undefined ? entries.slice(0, Math.max(0, maxEntries)) : entries;
	return selected
		.map((entry) => {
			try {
				return readManifest(path.join(resolveRealContainedPath(runsRoot, entry), DEFAULT_PATHS.state.manifestFile));
			} catch {
				return undefined;
			}
		})
		.filter((manifest): manifest is TeamRunManifest => manifest !== undefined);
}

function mergeRuns(runSets: TeamRunManifest[][], max?: number): TeamRunManifest[] {
	const byId = new Map<string, TeamRunManifest>();
	for (const runs of runSets) for (const run of runs) byId.set(run.runId, run);
	const sorted = [...byId.values()].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
	return max !== undefined ? sorted.slice(0, Math.max(0, max)) : sorted;
}

function scopedRunRoots(cwd: string): string[] {
	const roots = new Set<string>();
	roots.add(userCrewRoot());
	const projectRoot = findRepoRoot(cwd);
	if (projectRoot) roots.add(projectCrewRoot(cwd));
	return [...roots];
}

function collectActiveRuns(): TeamRunManifest[] {
	return activeRunEntries()
		.map((entry) => readManifest(entry.manifestPath))
		.filter((manifest): manifest is TeamRunManifest => manifest !== undefined);
}

export function listRuns(cwd: string): TeamRunManifest[] {
	const roots = scopedRunRoots(cwd);
	return mergeRuns([...roots.map((root) => collectRuns(root)), collectActiveRuns()]);
}

export function listRecentRuns(cwd: string, max = 20): TeamRunManifest[] {
	const roots = scopedRunRoots(cwd);
	return mergeRuns([...roots.map((root) => collectRuns(root, max)), collectActiveRuns()], max);
}

/**
 * List runs filtered to a specific scope.
 * - "project": only runs in the project crew root
 * - "user": only runs in the user crew root
 * - "all" (default): merge both scopes (current behavior)
 */
export function listRunsByScope(cwd: string, scope: "project" | "user" | "all" = "all", max?: number): TeamRunManifest[] {
	const projectRoot = findRepoRoot(cwd);
	switch (scope) {
		case "project":
			return projectRoot ? collectRuns(projectCrewRoot(cwd), max) : [];
		case "user":
			return collectRuns(userCrewRoot(), max);
		case "all":
		default:
			return max !== undefined ? listRecentRuns(cwd, max) : listRuns(cwd);
	}
}
