/**
 * sf-inturn-guard/index.js — Break degenerate agent loops within a single turn.
 *
 * Detects:
 *   1. Exact duplicate loop — same tool + params ≥3 times in a row
 *   2. Error retry loop — same tool fails ≥2 times with same params
 *   3. Yo-yo pattern — alternating read/edit on the same file within 30 s
 *
 * Intervenes via pi.sendMessage steer, which interrupts the current stream
 * after the current tool finishes.
 */
export default function sfInturnGuard(pi) {
	const MAX_BUFFER = 10;
	const COOLDOWN_TURNS = 3;

	const state = {
		calls: [],
		turnId: 0,
		interventionCount: 0,
		lastInterventionTurn: -1,
		enabled: true,
	};

	// ── Helpers ───────────────────────────────────────────────────────────────

	function addCall(toolName, input) {
		const entry = {
			name: toolName,
			input: JSON.stringify(input ?? {}),
			ts: Date.now(),
			isError: false,
			turnId: state.turnId,
		};
		state.calls.push(entry);
		if (state.calls.length > MAX_BUFFER) state.calls.shift();
		return entry;
	}

	function recentInTurn(n) {
		return state.calls.filter((c) => c.turnId === state.turnId).slice(-n);
	}

	function canIntervene() {
		if (!state.enabled) return false;
		if (state.interventionCount === 0) return true;
		return state.turnId - state.lastInterventionTurn >= COOLDOWN_TURNS;
	}

	function sendIntervention(reason, detail) {
		const text =
			`⚠️ Loop guard: ${reason}\n\n` +
			`${detail}\n\n` +
			`Stop repeating the same action. If stuck, try a different approach or ask for help.`;
		pi.sendMessage(
			{
				customType: "sf-inturn-guard",
				content: text,
				display: `[loop-guard] ${reason}`,
			},
			{ triggerTurn: true },
		);
	}

	// ── Detectors ─────────────────────────────────────────────────────────────

	function detectDuplicateLoop() {
		const recent = recentInTurn(5);
		if (recent.length < 3) return null;
		const last = recent[recent.length - 1];
		let count = 1;
		for (let i = recent.length - 2; i >= 0; i--) {
			if (recent[i].name === last.name && recent[i].input === last.input) {
				count++;
			} else {
				break;
			}
		}
		if (count >= 3) {
			return {
				reason: `${last.name} repeated ${count}× with identical args`,
				detail: `You've called \`${last.name}\` ${count} times with the same parameters. The result won't change.`,
			};
		}
		return null;
	}

	function detectErrorRetry() {
		const recent = recentInTurn(4);
		if (recent.length < 2) return null;
		const last = recent[recent.length - 1];
		if (!last.isError) return null;
		let count = 1;
		for (let i = recent.length - 2; i >= 0; i--) {
			const c = recent[i];
			if (c.name === last.name && c.input === last.input && c.isError) {
				count++;
			} else {
				break;
			}
		}
		if (count >= 2) {
			return {
				reason: `${last.name} failed ${count}× with identical args`,
				detail: `\`${last.name}\` has failed ${count} times with the same parameters. Fix the root cause before retrying.`,
			};
		}
		return null;
	}

	function detectYoYo() {
		const recent = recentInTurn(6);
		if (recent.length < 4) return null;
		const n = recent.length;
		for (let start = n - 4; start >= 0; start--) {
			const [c0, c1, c2, c3] = [
				recent[start],
				recent[start + 1],
				recent[start + 2],
				recent[start + 3],
			];
			if (c3.ts - c0.ts > 30000) continue;
			const isReadEdit = (name) => name === "read" || name === "edit";
			if (
				!isReadEdit(c0.name) ||
				!isReadEdit(c1.name) ||
				!isReadEdit(c2.name) ||
				!isReadEdit(c3.name)
			) {
				continue;
			}
			if (c0.name === c2.name && c1.name === c3.name && c0.name !== c1.name) {
				try {
					const p0 = JSON.parse(c0.input).path ?? "";
					const p1 = JSON.parse(c1.input).path ?? "";
					const p2 = JSON.parse(c2.input).path ?? "";
					const p3 = JSON.parse(c3.input).path ?? "";
					if (p0 && p0 === p1 && p1 === p2 && p2 === p3) {
						return {
							reason: `Yo-yo pattern on \`${p0}\``,
							detail: `You're alternating \`${c0.name}\` and \`${c1.name}\` on the same file. Decide the change, make it once, and move on.`,
						};
					}
				} catch {
					/* ignore parse errors */
				}
			}
		}
		return null;
	}

	// ── Event hooks ───────────────────────────────────────────────────────────

	pi.on("agent_start", () => {
		state.calls = [];
		state.turnId = 0;
		state.interventionCount = 0;
		state.lastInterventionTurn = -1;
	});

	pi.on("turn_start", () => {
		state.turnId++;
		state.calls = state.calls.slice(-MAX_BUFFER);
	});

	pi.on("tool_call", async (event, ctx) => {
		addCall(event.toolName, event.input);

		// Footer status
		if (ctx.hasUI) {
			const recent = recentInTurn(3)
				.map((c) => c.name)
				.join(" → ");
			ctx.ui.setStatus("sf-inturn-guard", `${recent} | T${state.turnId}`);
		}

		if (!canIntervene()) return;

		let detection = detectDuplicateLoop();
		if (!detection) detection = detectErrorRetry();
		if (!detection) detection = detectYoYo();

		if (detection) {
			state.interventionCount++;
			state.lastInterventionTurn = state.turnId;
			if (ctx.hasUI) {
				ctx.ui.notify(`🛑 ${detection.reason}`, "warning");
			}
			sendIntervention(detection.reason, detection.detail);
		}
	});

	pi.on("tool_result", async (event) => {
		if (event.isError) {
			for (let i = state.calls.length - 1; i >= 0; i--) {
				const c = state.calls[i];
				if (c.turnId === state.turnId && c.name === event.toolName) {
					c.isError = true;
					break;
				}
			}
		}
	});

	pi.on("agent_end", () => {
		state.calls = [];
		if (state.interventionCount > 0) {
			pi.appendEntry("sf-inturn-guard", {
				interventions: state.interventionCount,
				lastInterventionTurn: state.lastInterventionTurn,
			});
		}
	});

	// ── Commands ──────────────────────────────────────────────────────────────

	pi.registerCommand("guard-toggle", {
		description: "Toggle in-turn loop guard on/off",
		handler: async (_args, ctx) => {
			state.enabled = !state.enabled;
			const status = state.enabled ? "ON" : "OFF";
			if (ctx.hasUI) {
				ctx.ui.notify(
					`🛡 In-turn guard ${status}`,
					state.enabled ? "info" : "warning",
				);
			}
		},
	});

	pi.registerCommand("guard-status", {
		description: "Show in-turn guard statistics",
		handler: async (_args, ctx) => {
			const lines = [
				"╭─ In-Turn Guard ─╮",
				`Enabled: ${state.enabled ? "yes" : "no"}`,
				`Turn: ${state.turnId}`,
				`Interventions: ${state.interventionCount}`,
				`Last: ${state.lastInterventionTurn >= 0 ? `turn ${state.lastInterventionTurn}` : "never"}`,
				"╰─────────────────╯",
			];
			if (ctx.hasUI) ctx.ui.notify(lines.join("\n"), "info");
		},
	});
}
