/**
 * Pi DevOps — Optimized extension for CharityRight/QuikCue infrastructure
 *
 * Features:
 *   🏗️  Infra context   — injects full topology into every AI prompt
 *   🛡️  Safety guard    — confirms destructive ops before execution
 *   📊  Smart footer    — model, context, cost, tool tally, server indicator
 *   🎯  Focus widget    — keeps current task visible at all times
 *   ⚡  /health         — live container + service status check
 *   📋  /logs [svc]     — tail logs for a service
 *   🗺️  /infra          — print full infra topology
 *   🎯  /focus [task]   — set/change the current task
 *   🛑  /stop           — abort current AI action
 *   🔧  /ssh [cmd]      — run a raw SSH command on the server
 *
 * Usage: pi -e extensions/pi-devops.ts
 */

import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
import type { AssistantMessage } from "@mariozechner/pi-ai";
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
import { basename } from "node:path";
import { execSync } from "node:child_process";
import { applyExtensionDefaults } from "./themeMap.ts";

// ═══════════════════════════════════════════════════════════════════════════
// Infra constants
// ═══════════════════════════════════════════════════════════════════════════

const SSH = `ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -p 22 root@159.195.60.33`;
const INCUS_CR = `${SSH} "incus exec cr-server-new --"`;

const INFRA_CONTEXT = `
<infra-topology>
You are Pi, a DevOps assistant managing live production infrastructure.

## Primary Server
- SSH: ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -p 22 root@159.195.60.33 "<cmd>"
- HAProxy routes: charityright domains → cr-server-new, quikcue domains → qc-server-new

## Incus Containers (on primary server)
- cr-server-new (10.213.16.224) — CharityRight. Docker runs INSIDE here.
- qc-server-new (10.213.16.234) — QuikCue
- qc-server — STOPPED legacy

## Docker (inside cr-server-new only!)
- Run: ssh ... "incus exec cr-server-new -- docker <cmd>"
- Postgres container: dokploy-migrated-cr-postgres-data
- DB: donation_warehouse
- Query: ssh ... "incus exec cr-server-new -- docker exec dokploy-migrated-cr-postgres-data psql -U postgres -d donation_warehouse -c \\"SELECT ...\\""

## Services
- /opt/ayn-antivirus          — AYN antivirus scanner
- /opt/enthuse-db-sync-v2     — Enthuse donation sync
- /opt/launchgood-sync        — LaunchGood sync
- /root/legacy-donation-system-laravel — CharityRight Laravel app
- /root/redis-v2              — Redis

## Critical Rules
- NEVER run docker commands directly on the primary host
- Always use incus exec cr-server-new -- for Docker ops
- Warn before any restart/stop of production services
- Always test with SELECT before UPDATE/DELETE
</infra-topology>`.trim();

// ═══════════════════════════════════════════════════════════════════════════
// Dangerous patterns
// ═══════════════════════════════════════════════════════════════════════════

const DANGEROUS: { pattern: RegExp; reason: string }[] = [
	{ pattern: /rm\s+-[rRf]{1,3}\s+[\/~]/, reason: "Deleting from root or home!" },
	{ pattern: /rm\s+-rf/, reason: "Recursive force delete" },
	{ pattern: /DROP\s+(TABLE|DATABASE|SCHEMA)/i, reason: "SQL DROP detected" },
	{ pattern: /TRUNCATE\s+TABLE/i, reason: "SQL TRUNCATE detected" },
	{ pattern: /DELETE\s+FROM\s+\w+\s*(WHERE\s+1=1\s*)?(;|$)/i, reason: "Potentially unsafe DELETE" },
	{ pattern: /UPDATE\s+\w+\s+SET\s+.*\s*;?\s*$/i, reason: "UPDATE without WHERE — may affect all rows" },
	{ pattern: /docker\s+system\s+prune/, reason: "Docker prune — removes all unused data" },
	{ pattern: /incus\s+(delete|stop)\s+\S+/, reason: "Incus container operation" },
	{ pattern: /systemctl\s+(stop|disable|restart)\s+(haproxy|docker|nginx)/, reason: "Touching critical production service" },
	{ pattern: /git\s+push\s+.*--force/, reason: "Force push — may overwrite remote history" },
	{ pattern: /git\s+reset\s+--hard/, reason: "Hard reset — may lose uncommitted changes" },
	{ pattern: /mkfs\./, reason: "Filesystem format command!" },
	{ pattern: /dd\s+if=/, reason: "Raw disk copy via dd" },
	{ pattern: /curl.*\|\s*(ba)?sh/, reason: "Piping remote script to shell" },
	{ pattern: /wget.*\|\s*(ba)?sh/, reason: "Piping remote script to shell" },
	{ pattern: /chmod\s+-R\s+777/, reason: "Recursive world-writable permissions" },
];

// ═══════════════════════════════════════════════════════════════════════════
// Helpers
// ═══════════════════════════════════════════════════════════════════════════

function runSSH(cmd: string, timeout = 15000): string {
	try {
		return execSync(`${SSH} "${cmd.replace(/"/g, '\\"')}"`, { timeout, encoding: "utf8" }).trim();
	} catch (e: any) {
		return e?.stdout?.trim() || e?.message || "SSH error";
	}
}

// ═══════════════════════════════════════════════════════════════════════════
// Extension
// ═══════════════════════════════════════════════════════════════════════════

export default function (pi: ExtensionAPI) {
	let focus: string | undefined;
	let activeCtx: ExtensionContext | undefined;
	const toolCounts: Record<string, number> = {};

	// ── Tool tracking ─────────────────────────────────────────────────────
	pi.on("tool_execution_end", async (event) => {
		toolCounts[event.toolName] = (toolCounts[event.toolName] || 0) + 1;
	});

	// ── Session start ─────────────────────────────────────────────────────
	pi.on("session_start", async (_event, ctx) => {
		applyExtensionDefaults(import.meta.url, ctx);
		activeCtx = ctx;

		// Ask for focus task (non-blocking)
		void (async () => {
			const answer = await ctx.ui.input(
				"🎯 What are you working on today?",
				"e.g. Debug launchgood sync, check cr-server disk, investigate slow queries"
			);
			if (answer?.trim()) {
				focus = answer.trim();
				setFocusWidget(ctx);
				ctx.ui.notify(`🎯 Focus set: ${focus}`, "info");
			} else {
				ctx.ui.notify("💡 No focus set. Use /focus to set one anytime.", "info");
			}
		})();

		// ── Footer ────────────────────────────────────────────────────────
		ctx.ui.setFooter((tui, theme, footerData) => {
			const unsub = footerData.onBranchChange(() => tui.requestRender());
			return {
				dispose: unsub,
				invalidate() {},
				render(width: number): string[] {
					// Accumulate tokens + cost
					let tokIn = 0, tokOut = 0, cost = 0;
					for (const entry of ctx.sessionManager.getBranch()) {
						if (entry.type === "message" && entry.message.role === "assistant") {
							const m = entry.message as AssistantMessage;
							tokIn += m.usage.input;
							tokOut += m.usage.output;
							cost += m.usage.cost.total;
						}
					}

					const fmt = (n: number) => n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`;
					const model = ctx.model?.id || "no-model";
					const branch = footerData.getGitBranch();
					const dir = basename(ctx.cwd);

					// Context bar
					const usage = ctx.getContextUsage();
					const pct = usage?.percent ?? 0;
					const filled = Math.round(pct / 10);
					const bar =
						(pct < 70 ? theme.fg("success", "█".repeat(filled)) : pct < 90 ? theme.fg("warning", "█".repeat(filled)) : theme.fg("error", "█".repeat(filled))) +
						theme.fg("dim", "░".repeat(10 - filled));

					// Line 1: model | context | tokens | cost
					const l1Left =
						theme.fg("accent", " Pi") +
						theme.fg("dim", ` ${model} `) +
						bar +
						theme.fg("dim", ` ${Math.round(pct)}%`);

					const l1Right =
						theme.fg("success", fmt(tokIn)) +
						theme.fg("dim", "↓ ") +
						theme.fg("accent", fmt(tokOut)) +
						theme.fg("dim", "↑ ") +
						theme.fg("warning", `$${cost.toFixed(4)}`) +
						theme.fg("dim", " ");

					const pad1 = " ".repeat(Math.max(1, width - visibleWidth(l1Left) - visibleWidth(l1Right)));
					const line1 = truncateToWidth(l1Left + pad1 + l1Right, width, "");

					// Line 2: cwd | branch | tools
					const l2Left =
						theme.fg("dim", ` ${dir}`) +
						(branch
							? theme.fg("dim", " ") + theme.fg("warning", "(") + theme.fg("success", branch) + theme.fg("warning", ")")
							: "") +
						theme.fg("dim", " › 159.195.60.33");

					const entries = Object.entries(toolCounts);
					const l2Right = entries.length === 0
						? theme.fg("dim", "no tools yet ")
						: entries.map(([n, c]) => theme.fg("accent", n) + theme.fg("dim", ":") + theme.fg("success", `${c}`)).join(theme.fg("dim", " ")) + " ";

					const pad2 = " ".repeat(Math.max(1, width - visibleWidth(l2Left) - visibleWidth(l2Right)));
					const line2 = truncateToWidth(l2Left + pad2 + l2Right, width, "");

					return [line1, line2];
				},
			};
		});
	});

	pi.on("session_switch", async (_event, ctx) => { activeCtx = ctx; });

	// ── Safety guard ──────────────────────────────────────────────────────
	pi.on("tool_call", async (event, ctx) => {
		if (isToolCallEventType("bash", event)) {
			const cmd = event.input.command;
			for (const { pattern, reason } of DANGEROUS) {
				if (pattern.test(cmd)) {
					const confirmed = await ctx.ui.confirm(
						"⚠️ Destructive Operation",
						`${reason}\n\nCommand:\n${cmd}\n\nRun this on LIVE production?`,
						{ timeout: 30000 }
					);
					if (!confirmed) {
						ctx.ui.notify(`🛡️ Blocked: ${reason}`, "warning");
						ctx.abort();
						return {
							block: true,
							reason: `🛑 BLOCKED: ${reason}\n\nUser declined. Ask what to do instead.`,
						};
					}
					break;
				}
			}
		}
		return { block: false };
	});

	// ── System prompt injection ───────────────────────────────────────────
	pi.on("before_agent_start", async (event) => {
		let extra = `\n\n${INFRA_CONTEXT}`;
		if (focus) {
			extra += `\n\n<current-focus>\n${focus}\nStay focused on this task. If asked something unrelated, note it but bring back to the focus.\n</current-focus>`;
		}
		return { systemPrompt: event.systemPrompt + extra };
	});

	// ── Focus widget ──────────────────────────────────────────────────────
	function setFocusWidget(ctx: ExtensionContext) {
		ctx.ui.setWidget("focus", () => ({
			render(width: number): string[] {
				const line = truncateToWidth(`  🎯 ${focus}`, width - 2, "…");
				return [
					" ".repeat(width),
					line + " ".repeat(Math.max(0, width - visibleWidth(line))),
					" ".repeat(width),
				];
			},
			invalidate() {},
		}));
	}

	// ═══════════════════════════════════════════════════════════════════════
	// Commands
	// ═══════════════════════════════════════════════════════════════════════

	pi.registerCommand("stop", {
		description: "Abort current AI action",
		handler: async (args, ctx) => {
			ctx.abort();
			ctx.ui.notify(args?.trim() ? `🛑 Stopped: ${args.trim()}` : "🛑 Stopped.", "warning");
		},
	});

	pi.registerCommand("focus", {
		description: "Set or change current task focus",
		handler: async (args, ctx) => {
			if (args?.trim()) {
				focus = args.trim();
				setFocusWidget(ctx);
				ctx.ui.notify(`🎯 Focus set: ${focus}`, "info");
			} else {
				const answer = await ctx.ui.input("🎯 What are you working on?", focus || "e.g. Debug sync, check logs...");
				if (answer?.trim()) {
					focus = answer.trim();
					setFocusWidget(ctx);
					ctx.ui.notify(`🎯 Focus set: ${focus}`, "info");
				}
			}
		},
	});

	pi.registerCommand("infra", {
		description: "Show infra topology",
		handler: async (_args, ctx) => {
			ctx.ui.notify(
				"🗺️ Infra Topology\n\n" +
				"Server: root@159.195.60.33\n\n" +
				"Containers:\n" +
				"  cr-server-new (10.213.16.224) — CharityRight + Docker\n" +
				"  qc-server-new (10.213.16.234) — QuikCue\n" +
				"  qc-server                     — STOPPED\n\n" +
				"HAProxy:\n" +
				"  charityright.* → cr-server-new:443\n" +
				"  quikcue.*      → qc-server-new:443\n" +
				"  antivirus.quikcue.com → localhost:8877\n\n" +
				"Services (on cr-server-new):\n" +
				"  /opt/ayn-antivirus\n" +
				"  /opt/enthuse-db-sync-v2\n" +
				"  /opt/launchgood-sync\n" +
				"  /root/legacy-donation-system-laravel\n" +
				"  /root/redis-v2",
				"info"
			);
		},
	});

	pi.registerCommand("health", {
		description: "Live check of containers and key services",
		handler: async (_args, ctx) => {
			ctx.ui.notify("⏳ Checking infra health...", "info");
			try {
				const containers = runSSH("incus list --format=csv -c ns", 10000);
				const haproxy = runSSH("systemctl is-active haproxy", 5000);
				const docker = runSSH("incus exec cr-server-new -- docker ps --format '{{.Names}}\\t{{.Status}}' 2>/dev/null | head -10", 10000);
				const disk = runSSH("df -h / | tail -1 | awk '{print $5\" used of \"$2}'", 5000);
				const mem = runSSH("free -m | awk 'NR==2{printf \"%sMB / %sMB (%.0f%%)\", $3,$2,$3*100/$2}'", 5000);

				ctx.ui.notify(
					"✅ Infrastructure Health\n\n" +
					`HAProxy: ${haproxy === "active" ? "✅ active" : "❌ " + haproxy}\n\n` +
					`Containers:\n${containers.split("\n").map(l => "  " + l).join("\n")}\n\n` +
					`Docker (cr-server-new):\n${docker.split("\n").map(l => "  " + l).join("\n")}\n\n` +
					`Disk: ${disk}\n` +
					`RAM:  ${mem}`,
					"info"
				);
			} catch (e: any) {
				ctx.ui.notify(`❌ Health check failed: ${e.message}`, "error");
			}
		},
	});

	pi.registerCommand("logs", {
		description: "Tail logs for a service (usage: /logs launchgood)",
		handler: async (args, ctx) => {
			const services: Record<string, string> = {
				launchgood: "incus exec cr-server-new -- journalctl -u launchgood-sync -n 50 --no-pager 2>/dev/null || incus exec cr-server-new -- tail -50 /opt/launchgood-sync/logs/app.log 2>/dev/null",
				enthuse: "incus exec cr-server-new -- journalctl -u enthuse-sync -n 50 --no-pager 2>/dev/null || incus exec cr-server-new -- tail -50 /opt/enthuse-db-sync-v2/logs/app.log 2>/dev/null",
				antivirus: "journalctl -u ayn-antivirus -n 50 --no-pager 2>/dev/null",
				haproxy: "journalctl -u haproxy -n 50 --no-pager",
				laravel: "incus exec cr-server-new -- tail -50 /root/legacy-donation-system-laravel/storage/logs/laravel.log 2>/dev/null",
			};

			const svc = args?.trim().toLowerCase();

			if (!svc || !services[svc]) {
				const opts = Object.keys(services);
				const choice = await ctx.ui.select("Which service logs?", opts);
				if (!choice) return;
				const cmd = services[choice];
				ctx.ui.notify("⏳ Fetching logs...", "info");
				const out = runSSH(cmd, 15000);
				ctx.ui.notify(`📋 ${choice} logs:\n\n${out.slice(-3000)}`, "info");
			} else {
				ctx.ui.notify("⏳ Fetching logs...", "info");
				const out = runSSH(services[svc], 15000);
				ctx.ui.notify(`📋 ${svc} logs:\n\n${out.slice(-3000)}`, "info");
			}
		},
	});

	pi.registerCommand("ssh", {
		description: "Run a raw SSH command on the server",
		handler: async (args, ctx) => {
			if (!args?.trim()) {
				ctx.ui.notify("Usage: /ssh <command>\nExample: /ssh incus list", "warning");
				return;
			}
			ctx.ui.notify(`⏳ Running: ${args.trim()}`, "info");
			const out = runSSH(args.trim(), 20000);
			ctx.ui.notify(`$ ${args.trim()}\n\n${out}`, "info");
		},
	});
}
