/**
 * /tasks command — unified background work surface
 *
 * Purpose: show all background work in one view: autonomous units, parallel
 * workers, scheduled dispatches, shell sessions, stuck sessions, remote
 * questions, cost/budget state, and last checkpoint.
 *
 * Consumer: human users via TUI and web chat mode.
 */
import { closeDatabase, getDatabase, isDbAvailable } from "../../sf-db.js";
import { projectRoot } from "../context.js";

const TASK_STATE_ICONS = {
	todo: "○",
	in_progress: "◐",
	review: "◑",
	done: "●",
	retrying: "↻",
	failed: "✗",
	cancelled: "⊘",
};

const TASK_STATE_LABELS = {
	todo: "todo",
	in_progress: "running",
	review: "review",
	done: "done",
	retrying: "retrying",
	failed: "failed",
	cancelled: "cancelled",
};

/**
 * Format a task row for display.
 */
function formatTaskLine(task, width = 80) {
	const icon = TASK_STATE_ICONS[task.state] ?? "?";
	const label = TASK_STATE_LABELS[task.state] ?? task.state;
	const id = task.unitId ?? task.id ?? "unknown";
	const title = task.title ?? id;
	const duration = task.durationMs
		? ` ${Math.round(task.durationMs / 1000)}s`
		: "";
	const cost = task.costUsd ? ` $${task.costUsd.toFixed(2)}` : "";
	const worker = task.workerId ? ` [${task.workerId}]` : "";

	const right = `${duration}${cost}${worker}`;
	const maxTitle = Math.max(20, width - right.length - 8);
	const truncated =
		title.length > maxTitle ? title.slice(0, maxTitle - 1) + "…" : title;

	return `${icon} ${truncated.padEnd(maxTitle)} ${label.padStart(10)}${right}`;
}

/**
 * Build the /tasks text report.
 */
function buildTasksReport({ tasks, summary, mode, budget }) {
	const lines = [];
	const now = new Date().toISOString().slice(0, 19).replace("T", " ");

	lines.push(`SF Background Work  ${now}`);
	lines.push("");

	// Mode line
	if (mode) {
		lines.push(
			`Mode: ${mode.workMode} · ${mode.runControl} · ${mode.permissionProfile} · ${mode.modelMode}`,
		);
		lines.push("");
	}

	// Summary
	if (summary) {
		const { counts, total, progress, isComplete } = summary;
		const parts = [];
		for (const [state, count] of Object.entries(counts)) {
			if (count > 0) parts.push(`${TASK_STATE_ICONS[state] ?? "?"}${count}`);
		}
		lines.push(
			`Tasks: ${total} total · ${parts.join(" ")} · ${progress}% complete${isComplete ? " ✓" : ""}`,
		);
		lines.push("");
	}

	// Active tasks
	const active = tasks.filter((t) =>
		["in_progress", "review", "retrying"].includes(t.state),
	);
	if (active.length > 0) {
		lines.push(`Active (${active.length}):`);
		for (const task of active) {
			lines.push(`  ${formatTaskLine(task)}`);
		}
		lines.push("");
	}

	// Pending
	const pending = tasks.filter((t) => t.state === "todo");
	if (pending.length > 0) {
		lines.push(`Pending (${pending.length}):`);
		for (const task of pending.slice(0, 10)) {
			lines.push(`  ${formatTaskLine(task)}`);
		}
		if (pending.length > 10) {
			lines.push(`  … and ${pending.length - 10} more`);
		}
		lines.push("");
	}

	// Terminal
	const terminal = tasks.filter((t) =>
		["done", "failed", "cancelled"].includes(t.state),
	);
	if (terminal.length > 0) {
		const recent = terminal
			.sort((a, b) => (b.endedAt ?? "").localeCompare(a.endedAt ?? ""))
			.slice(0, 5);
		lines.push(`Recently finished (${terminal.length} total):`);
		for (const task of recent) {
			lines.push(`  ${formatTaskLine(task)}`);
		}
		lines.push("");
	}

	// Budget
	if (budget) {
		lines.push(
			`Budget: $${budget.spent.toFixed(2)} / $${budget.ceiling.toFixed(2)} · ${budget.remaining > 0 ? "$" + budget.remaining.toFixed(2) + " remaining" : "OVER BUDGET"}`,
		);
		lines.push("");
	}

	lines.push("Commands: /tasks --refresh  /tasks --failed  /tasks --cancelled");
	return lines.join("\n");
}

function normalizeDbTask(row) {
	return {
		...row,
		id: row.id,
		unitId: row.unitId ?? row.unit_id ?? row.id,
		milestoneId: row.milestoneId ?? row.milestone_id ?? null,
		sliceId: row.sliceId ?? row.slice_id ?? null,
		taskId: row.taskId ?? row.task_id ?? null,
		title: row.title ?? row.unit_id ?? row.id,
		state: row.state ?? row.status ?? "todo",
		workerId: row.workerId ?? row.worker_id ?? null,
		durationMs: row.durationMs ?? row.duration_ms ?? null,
		costUsd: row.costUsd ?? row.cost_usd ?? null,
		endedAt: row.endedAt ?? row.ended_at ?? null,
		graphId: row.graphId ?? row.graph_id ?? null,
	};
}

/**
 * Handle the /tasks command.
 */
export async function handleTasks(args, ctx) {
	const basePath = projectRoot();
	const trimmed = args.trim().toLowerCase();

	// Parse flags
	const refresh = trimmed.includes("--refresh") || trimmed.includes("-r");
	const showFailed = trimmed.includes("--failed") || trimmed.includes("-f");
	const showCancelled =
		trimmed.includes("--cancelled") || trimmed.includes("-c");
	const showAll = trimmed.includes("--all") || trimmed.includes("-a");

	const { ensureDbOpen } = await import("../../bootstrap/dynamic-tools.js");
	if (refresh) closeDatabase();
	await ensureDbOpen();

	// Get mode state
	const { getAutoSession } = await import("../../auto/session.js");
	const session = getAutoSession();
	const mode = session?.getMode?.() ?? null;

	// Try DB-backed task query first
	let tasks = [];
	let summary = null;

	if (isDbAvailable()) {
		try {
			const db = getDatabase();
			if (!db) throw new Error("SF database is not open");
			const { queryTasksByState, getGraphStateSummary } = await import(
				"../../uok/execution-graph-persist.js"
			);

			let states = [];
			if (showFailed) states = ["failed"];
			else if (showCancelled) states = ["cancelled"];
			else if (!showAll) states = ["todo", "in_progress", "review", "retrying"];

			tasks = queryTasksByState(db, { states, limit: 200 }).map(
				normalizeDbTask,
			);

			// Get summary for active graphs
			const graphIds = [
				...new Set(tasks.map((t) => t.graphId).filter(Boolean)),
			];
			if (graphIds.length > 0) {
				// Use first active graph for summary
				summary = getGraphStateSummary(db, graphIds[0]);
			}
		} catch (err) {
			// DB query failed — fall through to filesystem fallback
			ctx.ui.notify(
				`DB query failed: ${err.message}. Falling back to filesystem.`,
				"warning",
			);
		}
	}

	// Fallback: derive from unit runtime files if no DB tasks
	if (tasks.length === 0) {
		try {
			const { deriveState } = await import("../../state.js");
			const state = await deriveState(basePath);

			// Convert milestone registry entries to task records
			for (const ms of state.registry) {
				for (const slice of ms.slices ?? []) {
					for (const task of slice.tasks ?? []) {
						tasks.push({
							id: `${ms.id}/${slice.id}/${task.id}`,
							unitId: `${ms.id}/${slice.id}/${task.id}`,
							milestoneId: ms.id,
							sliceId: slice.id,
							taskId: task.id,
							title: task.title ?? task.id,
							state: task.done
								? "done"
								: task.inProgress
									? "in_progress"
									: "todo",
							updatedAt: task.updatedAt ?? null,
						});
					}
				}
			}

			// Compute summary
			const states = tasks.map((t) => t.state);
			const counts = {};
			for (const s of states) counts[s] = (counts[s] ?? 0) + 1;
			const total = tasks.length;
			const terminal = (counts.done ?? 0) + (counts.failed ?? 0);
			summary = {
				counts: {
					todo: counts.todo ?? 0,
					in_progress: counts.in_progress ?? 0,
					review: counts.review ?? 0,
					done: counts.done ?? 0,
					retrying: counts.retrying ?? 0,
					failed: counts.failed ?? 0,
					cancelled: counts.cancelled ?? 0,
				},
				total,
				terminal,
				progress: total > 0 ? Math.round((terminal / total) * 100) : 0,
				isComplete: terminal === total && total > 0,
			};
		} catch (err) {
			ctx.ui.notify(`State derivation failed: ${err.message}`, "error");
		}
	}

	// Budget estimate
	let budget = null;
	try {
		const { getGlobalSFPreferences } = await import("../../preferences.js");
		const prefs = getGlobalSFPreferences();
		const ceiling = prefs.budget_ceiling ?? 0;
		if (ceiling > 0) {
			// Estimate spent from task costs
			const spent = tasks.reduce((sum, t) => sum + (t.costUsd ?? 0), 0);
			budget = {
				spent,
				ceiling,
				remaining: ceiling - spent,
			};
		}
	} catch {
		// Budget info is best-effort
	}

	const report = buildTasksReport({ tasks, summary, mode, budget });
	ctx.ui.notify(report, "info");
}
