import { readdir } from "node:fs/promises";
import { cpus, homedir, totalmem } from "node:os";
import { execSync } from "node:child_process";
import { resolve as resolvePath } from "node:path";

import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";

import {
	APP_ROOT,
	FEYNMAN_AGENT_LOGO,
	FEYNMAN_VERSION,
} from "./shared.js";

const ANSI_RE = /\x1b\[[0-9;]*m/g;

function visibleLength(text: string): number {
	return text.replace(ANSI_RE, "").length;
}

function formatHeaderPath(path: string): string {
	const home = homedir();
	return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
}

function truncateVisible(text: string, maxVisible: number): string {
	const raw = text.replace(ANSI_RE, "");
	if (raw.length <= maxVisible) return text;
	if (maxVisible <= 3) return ".".repeat(maxVisible);
	return `${raw.slice(0, maxVisible - 3)}...`;
}

function wrapWords(text: string, maxW: number): string[] {
	const words = text.split(" ");
	const lines: string[] = [];
	let cur = "";
	for (let word of words) {
		if (word.length > maxW) {
			if (cur) { lines.push(cur); cur = ""; }
			word = maxW > 3 ? `${word.slice(0, maxW - 1)}…` : word.slice(0, maxW);
		}
		const test = cur ? `${cur} ${word}` : word;
		if (cur && test.length > maxW) {
			lines.push(cur);
			cur = word;
		} else {
			cur = test;
		}
	}
	if (cur) lines.push(cur);
	return lines.length ? lines : [""];
}

function padRight(text: string, width: number): string {
	const gap = Math.max(0, width - visibleLength(text));
	return `${text}${" ".repeat(gap)}`;
}

function centerText(text: string, width: number): string {
	if (text.length >= width) return text.slice(0, width);
	const left = Math.floor((width - text.length) / 2);
	const right = width - text.length - left;
	return `${" ".repeat(left)}${text}${" ".repeat(right)}`;
}

function getCurrentModelLabel(ctx: ExtensionContext): string {
	if (ctx.model) return `${ctx.model.provider}/${ctx.model.id}`;
	const branch = ctx.sessionManager.getBranch();
	for (let index = branch.length - 1; index >= 0; index -= 1) {
		const entry = branch[index]!;
		if (entry.type === "model_change") return `${(entry as any).provider}/${(entry as any).modelId}`;
	}
	return "not set";
}

function extractMessageText(message: unknown): string {
	if (!message || typeof message !== "object") return "";
	const content = (message as { content?: unknown }).content;
	if (typeof content === "string") return content;
	if (!Array.isArray(content)) return "";
	return content
		.map((item) => {
			if (!item || typeof item !== "object") return "";
			const record = item as { type?: string; text?: unknown; name?: unknown };
			if (record.type === "text" && typeof record.text === "string") return record.text;
			if (record.type === "toolCall") return `[${typeof record.name === "string" ? record.name : "tool"}]`;
			return "";
		})
		.filter(Boolean)
		.join(" ");
}

function getRecentActivitySummary(ctx: ExtensionContext): string {
	const branch = ctx.sessionManager.getBranch();
	for (let index = branch.length - 1; index >= 0; index -= 1) {
		const entry = branch[index]!;
		if (entry.type !== "message") continue;
		const msg = entry as any;
		const text = extractMessageText(msg.message).replace(/\s+/g, " ").trim();
		if (!text) continue;
		const role = msg.message.role === "assistant" ? "agent" : msg.message.role === "user" ? "you" : msg.message.role;
		return `${role}: ${text}`;
	}
	return "";
}

async function buildAgentCatalogSummary(): Promise<{ agents: string[]; chains: string[] }> {
	const agents: string[] = [];
	const chains: string[] = [];
	try {
		const entries = await readdir(resolvePath(APP_ROOT, ".feynman", "agents"), { withFileTypes: true });
		for (const entry of entries) {
			if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
			if (entry.name.endsWith(".chain.md")) {
				chains.push(entry.name.replace(/\.chain\.md$/i, ""));
			} else {
				agents.push(entry.name.replace(/\.md$/i, ""));
			}
		}
	} catch {
		return { agents: [], chains: [] };
	}
	agents.sort();
	chains.sort();
	return { agents, chains };
}

type SystemResources = {
	cpu: string;
	cores: number;
	ramTotal: string;
	ramFree: string;
	gpu: string | null;
	docker: boolean;
};

let cachedResources: SystemResources | null = null;

function detectSystemResources(): SystemResources {
	if (cachedResources) return cachedResources;

	const cores = cpus().length;
	const totalBytes = totalmem();
	const ramTotal = `${Math.round(totalBytes / (1024 ** 3))}GB`;

	cachedResources = { cpu: "", cores, ramTotal, ramFree: "", gpu: null, docker: false };

	try {
		if (process.platform === "darwin") {
			const out = execSync("sysctl -n machdep.cpu.brand_string 2>/dev/null", { encoding: "utf8", timeout: 1000 }).trim();
			if (out) cachedResources.cpu = out;
		}
	} catch {}

	try {
		execSync("command -v docker >/dev/null 2>&1", { timeout: 500 });
		cachedResources.docker = true;
	} catch {}

	return cachedResources;
}

type WorkflowInfo = { name: string; description: string };

function getResearchWorkflows(pi: ExtensionAPI): WorkflowInfo[] {
	return pi.getCommands()
		.filter((cmd) => cmd.source === "prompt")
		.map((cmd) => ({ name: `/${cmd.name}`, description: cmd.description ?? "" }))
		.sort((a, b) => a.name.localeCompare(b.name));
}

function shortDescription(desc: string): string {
	const lower = desc.toLowerCase();
	for (const prefix of ["run a ", "run an ", "set up a ", "build a ", "build the ", "turn ", "design the ", "produce a ", "compare ", "simulate ", "inspect ", "write a ", "plan or execute a ", "prepare a "]) {
		if (lower.startsWith(prefix)) return desc.slice(prefix.length);
	}
	return desc;
}

export function installFeynmanHeader(
	pi: ExtensionAPI,
	ctx: ExtensionContext,
	cache: { agentSummaryPromise?: Promise<{ agents: string[]; chains: string[] }> },
): void | Promise<void> {
	if (!ctx.hasUI) return;

	cache.agentSummaryPromise ??= buildAgentCatalogSummary();

	return cache.agentSummaryPromise.then((agentData) => {
		const resources = detectSystemResources();
		const workflows = getResearchWorkflows(pi);
		const toolCount = pi.getAllTools().length;
		const commandCount = pi.getCommands().length;
		const agentCount = agentData.agents.length + agentData.chains.length;

		ctx.ui.setHeader((_tui, theme) => ({
			render(width: number): string[] {
				const maxW = Math.max(width - 2, 1);
				const cardW = Math.min(maxW, 120);
				const innerW = cardW - 2;
				const contentW = innerW - 2;
				const outerPad = " ".repeat(Math.max(0, Math.floor((width - cardW) / 2)));
				const lines: string[] = [];

				const push = (line: string) => { lines.push(`${outerPad}${line}`); };
				const border = (ch: string) => theme.fg("borderMuted", ch);

				const row = (content: string): string =>
					`${border("│")} ${padRight(content, contentW)} ${border("│")}`;
				const emptyRow = (): string =>
					`${border("│")}${" ".repeat(innerW)}${border("│")}`;
				const sep = (): string =>
					`${border("├")}${border("─".repeat(innerW))}${border("┤")}`;

				const useWideLayout = contentW >= 70;
				const leftW = useWideLayout ? Math.min(38, Math.floor(contentW * 0.35)) : 0;
				const divColW = useWideLayout ? 3 : 0;
				const rightW = useWideLayout ? contentW - leftW - divColW : contentW;

				const twoCol = (left: string, right: string): string => {
					if (!useWideLayout) return row(left || right);
					return row(
						`${padRight(left, leftW)}${border(" │ ")}${padRight(right, rightW)}`,
					);
				};

				const modelLabel = getCurrentModelLabel(ctx);
				const sessionId = ctx.sessionManager.getSessionName()?.trim() || ctx.sessionManager.getSessionId();
				const dirLabel = formatHeaderPath(ctx.cwd);
				const activity = getRecentActivitySummary(ctx);

				push("");
				if (cardW >= 70) {
					const maxLogoW = Math.max(...FEYNMAN_AGENT_LOGO.map((l) => l.length));
					const logoOffset = " ".repeat(Math.max(0, Math.floor((cardW - maxLogoW) / 2)));
					for (const logoLine of FEYNMAN_AGENT_LOGO) {
						push(theme.fg("accent", theme.bold(`${logoOffset}${truncateVisible(logoLine, cardW)}`)));
					}
					push("");
				}

				const versionTag = ` v${FEYNMAN_VERSION} `;
				const gap = Math.max(0, innerW - versionTag.length);
				const gapL = Math.floor(gap / 2);
				push(
					border(`╭${"─".repeat(gapL)}`) +
					theme.fg("dim", versionTag) +
					border(`${"─".repeat(gap - gapL)}╮`),
				);

				if (useWideLayout) {
					const cmdNameW = 16;
					const descW = Math.max(10, rightW - cmdNameW - 2);

					const leftValueW = Math.max(1, leftW - 11);
					const indent = " ".repeat(11);
					const leftLines: string[] = [""];

					const pushLabeled = (label: string, value: string, color: "text" | "dim") => {
						const wrapped = wrapWords(value, leftValueW);
						leftLines.push(`${theme.fg("dim", label.padEnd(10))} ${theme.fg(color, wrapped[0]!)}`);
						for (let i = 1; i < wrapped.length; i++) {
							leftLines.push(`${indent}${theme.fg(color, wrapped[i]!)}`);
						}
					};

					pushLabeled("model", modelLabel, "text");
					pushLabeled("directory", dirLabel, "text");
					pushLabeled("session", sessionId, "dim");
					leftLines.push("");
					const sysParts = [`${resources.cores} cores`, resources.ramTotal];
					if (resources.docker) sysParts.push("docker");
					pushLabeled("system", sysParts.join(" · "), "dim");
					leftLines.push("");
					leftLines.push(theme.fg("dim", `${toolCount} tools · ${agentCount} agents`));

					const pushList = (heading: string, items: string[]) => {
						if (items.length === 0) return;
						leftLines.push("");
						leftLines.push(theme.fg("accent", theme.bold(heading)));
						for (const line of wrapWords(items.join(", "), leftW)) {
							leftLines.push(theme.fg("dim", line));
						}
					};

					pushList("Agents", agentData.agents);
					pushList("Chains", agentData.chains);

					if (activity) {
						const maxActivityLen = leftW * 2;
						const trimmed = activity.length > maxActivityLen
							? `${activity.slice(0, maxActivityLen - 1)}…`
							: activity;
						leftLines.push("");
						leftLines.push(theme.fg("accent", theme.bold("Last Activity")));
						for (const line of wrapWords(trimmed, leftW)) {
							leftLines.push(theme.fg("dim", line));
						}
					}

					const rightLines: string[] = [
						"",
						theme.fg("accent", theme.bold("Research Workflows")),
					];

					for (const wf of workflows) {
						if (wf.name === "/jobs" || wf.name === "/log") continue;
						const desc = shortDescription(wf.description);
						const descWords = desc.split(" ");
						let line = "";
						let first = true;
						for (const word of descWords) {
							const test = line ? `${line} ${word}` : word;
							if (line && test.length > descW) {
								rightLines.push(
									first
										? `${theme.fg("accent", wf.name.padEnd(cmdNameW))}${theme.fg("dim", line)}`
										: `${" ".repeat(cmdNameW)}${theme.fg("dim", line)}`,
								);
								first = false;
								line = word;
							} else {
								line = test;
							}
						}
						if (line || first) {
							rightLines.push(
								first
									? `${theme.fg("accent", wf.name.padEnd(cmdNameW))}${theme.fg("dim", line)}`
									: `${" ".repeat(cmdNameW)}${theme.fg("dim", line)}`,
							);
						}
					}

					const maxRows = Math.max(leftLines.length, rightLines.length);
					for (let i = 0; i < maxRows; i++) {
						push(twoCol(leftLines[i] ?? "", rightLines[i] ?? ""));
					}
				} else {
					const narrowValW = Math.max(1, contentW - 11);
					push(emptyRow());
					push(row(`${theme.fg("dim", "model".padEnd(10))} ${theme.fg("text", truncateVisible(modelLabel, narrowValW))}`));
					push(row(`${theme.fg("dim", "directory".padEnd(10))} ${theme.fg("text", truncateVisible(dirLabel, narrowValW))}`));
					push(row(`${theme.fg("dim", "session".padEnd(10))} ${theme.fg("dim", truncateVisible(sessionId, narrowValW))}`));
					const resourceLine = `${resources.cores} cores · ${resources.ramTotal}${resources.docker ? " · docker" : ""}`;
					push(row(theme.fg("dim", truncateVisible(resourceLine, contentW))));
					push(row(theme.fg("dim", truncateVisible(`${toolCount} tools · ${agentCount} agents · ${commandCount} commands`, contentW))));
					push(emptyRow());

					push(sep());
					push(row(theme.fg("accent", theme.bold("Research Workflows"))));
					const narrowDescW = Math.max(1, contentW - 17);
					for (const wf of workflows) {
						if (wf.name === "/jobs" || wf.name === "/log") continue;
						const desc = shortDescription(wf.description);
						push(row(`${theme.fg("accent", wf.name.padEnd(16))} ${theme.fg("dim", truncateVisible(desc, narrowDescW))}`));
					}

					if (agentData.agents.length > 0 || agentData.chains.length > 0) {
						push(sep());
						push(row(theme.fg("accent", theme.bold("Agents & Chains"))));
						if (agentData.agents.length > 0) {
							push(row(theme.fg("dim", truncateVisible(`agents  ${agentData.agents.join(", ")}`, contentW))));
						}
						if (agentData.chains.length > 0) {
							push(row(theme.fg("dim", truncateVisible(`chains  ${agentData.chains.join(", ")}`, contentW))));
						}
					}
				}

				push(border(`╰${"─".repeat(innerW)}╯`));
				push("");
				return lines;
			},
			invalidate() {},
		}));
	});
}
