/**
 * ask-claude — Stream Claude agent reviews into pi.
 *
 * For AGENTS to use. Delegates to specialized Claude agents or raw models
 * for review, analysis, debugging, and second opinions.
 *
 * Tool (callable by the LLM):
 *   ask_claude(prompt, agent?, model?, question?, session_id?)
 *     agent      — any agent name from ~/.claude/agents/ (e.g. "plan_review", "code_review", "oracle", "debug")
 *     model      — model override: "opus", "sonnet", or full model ID
 *     question   — specific focus prepended as a review header
 *     session_id — resume a prior conversation (returned in every response)
 *
 * Commands:
 *   /review-plan  — editor → Claude Opus plan_review → inject result
 *   /review-code  — editor → Claude Sonnet code_review → inject result
 */

import { defineTool, getMarkdownTheme } from "@mariozechner/pi-coding-agent";
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "@mariozechner/pi-ai";
import { Box, Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
import {
	type ClaudeDetails,
	type Theme,
	renderToolCallLine,
	renderToolResultBox,
	renderToolBlock,
	formatUsage,
	buildEditDiff,
	formatAnthropicError,
	runClaude,
	type RunClaudeResult,
} from "../shared/claude-stream.js";

// =============================================================================
// Rendering (ask-claude specific — uses shared tool renderers)
// =============================================================================

function buildLabel(agent?: string, model?: string): string {
	if (agent) return `Claude [${agent}]`;
	if (model) return `Claude [${model}]`;
	return "Claude Sonnet";
}

// =============================================================================
// Tool definition
// =============================================================================

const AskClaudeParams = Type.Object({
	prompt: Type.String({
		description: "Full content to review/analyze. Include all relevant context: CLAUDE.md conventions, files explored, code or plan to review.",
	}),
	agent: Type.Optional(Type.String({
		description: "Agent name from ~/.claude/agents/ (e.g. 'plan_review', 'code_review', 'oracle', 'debug'). Omit to use model= directly.",
	})),
	model: Type.Optional(Type.String({
		description: "Model override: 'opus', 'sonnet', 'haiku', or a full model ID. When agent is set this overrides the agent's default. When agent is omitted this selects the model directly.",
	})),
	question: Type.Optional(Type.String({
		description: "Specific question or focus area prepended to the prompt (e.g. 'Focus on security', 'Are there race conditions?').",
	})),
	session_id: Type.Optional(Type.String({
		description: "Resume a prior conversation. Pass the session_id returned from a previous ask_claude call.",
	})),
});

const askClaudeTool = defineTool({
	name: "ask_claude",
	label: "Ask Claude",
	description: [
		"Ask a Claude agent or model for review, analysis, or a second opinion.",
		"agent=<name> runs a named agent from ~/.claude/agents/ (e.g. 'plan_review', 'code_review', 'oracle', 'debug').",
		"Use model= alone for free-form requests without an agent. Use question= to specify a focus.",
		"Pass session_id from a prior response to continue the same conversation across turns.",
		"CLAUDE.md and .claude/skills are loaded automatically from the project root.",
	].join(" "),
	promptSnippet: "Ask a Claude agent or model for review, analysis, or a second opinion",
	promptGuidelines: [
		"Use ask_claude(agent=<name>) to invoke a specialized agent — include all relevant context in the prompt.",
		"Use ask_claude(model='opus', question='...') for free-form deep analysis.",
		"Always include the artifact to review (plan, code, problem description) in the prompt.",
		"Pass session_id back from the previous response to continue the conversation.",
	],
	parameters: AskClaudeParams,

	async execute(_toolCallId, params, signal, onUpdate, ctx) {
		const fullPrompt = params.question
			? `## Review Focus\n\n${params.question}\n\n## Content\n\n${params.prompt}`
			: params.prompt;

		const label = buildLabel(params.agent, params.model);
		const details: ClaudeDetails = { label, done: false, blocks: [], finalText: "", isResume: !!params.session_id };

		try {
			const result: RunClaudeResult = await runClaude(fullPrompt, {
				agent: params.agent,
				model: params.model,
				sessionId: params.session_id,
				enrichEditDiffs: true,   // ask-claude wants diff enrichment
				cwd: ctx.cwd,
				signal,
				onUpdate: (partial) => {
					Object.assign(details, partial);
					onUpdate?.({
						content: [{ type: "text", text: details.finalText || "(thinking…)" }],
						details: { ...details },
					});
				},
			});

			details.done      = true;
			details.finalText = result.finalText;
			details.blocks    = result.blocks;
			details.sessionId = result.sessionId;
			details.costUsd   = result.costUsd;
			details.inputTokens  = result.inputTokens;
			details.outputTokens = result.outputTokens;
			details.cacheReadTokens  = result.cacheReadTokens;
			details.cacheWriteTokens = result.cacheWriteTokens;

			const sessionNote = result.sessionId
				? `\n\n---\n*session_id: \`${result.sessionId}\`*`
				: "";

			return {
				content: [{ type: "text", text: (result.finalText || "(no output)") + sessionNote }],
				details: { ...details },
			};
		} catch (err) {
			const errMsg = err instanceof Error ? err.message : String(err);
			details.done = true;
			details.finalText = `Error: ${errMsg}`;
			return {
				content: [{ type: "text", text: `**Claude error:** ${errMsg}` }],
				details: { ...details },
			};
		}
	},

	renderCall(args, theme, _ctx) {
		const label = buildLabel(args.agent, args.model);
		let text = theme.fg("toolTitle", theme.bold("ask_claude ")) + theme.fg("accent", `[${label}]`);
		if (args.question) {
			text += "\n  " + theme.fg("dim", theme.italic(args.question));
		} else {
			const lines = args.prompt.split("\n").filter((l) => l.trim()).slice(0, 3);
			text += "\n  " + theme.fg("dim", lines.join("\n  "));
		}
		return new Text(text, 0, 0);
	},

	renderResult(result, { isPartial }, theme, _ctx) {
		const d = result.details as ClaudeDetails | undefined;
		if (!d) return new Text(theme.fg("muted", "…"), 0, 0);

		const isDone = d.done && !isPartial;
		const statusIcon = isDone ? theme.fg("success", "✓ ") : theme.fg("warning", "⏳ ");
		const c = new Container();

		const resume = d.isResume ? theme.fg("dim", " ↩") : "";
		c.addChild(new Text(statusIcon + theme.fg("toolTitle", theme.bold(d.label)) + resume, 0, 0));

		for (const block of d.blocks ?? []) {
			if (block.type === "thinking" && block.text.trim()) {
				c.addChild(new Text(theme.fg("dim", theme.italic(block.text.trimEnd())), 0, 0));
			} else if (block.type === "tool") {
				c.addChild(renderToolBlock(block, theme as any));
			} else if (block.type === "text" && block.text.trim()) {
				c.addChild(new Spacer(1));
				if (isDone) {
					c.addChild(new Markdown(block.text.trim(), 0, 0, getMarkdownTheme()));
				} else {
					c.addChild(new Text(theme.fg("dim", block.text.trimEnd()), 0, 0));
				}
			}
		}

		if (isDone) {
			const usageLine = formatUsage(d);
			const parts: string[] = [];
			if (usageLine) parts.push(usageLine);
			if (d.sessionId) parts.push(`session:${d.sessionId.slice(0, 8)}`);
			if (parts.length > 0) {
				c.addChild(new Spacer(1));
				c.addChild(new Text(theme.fg("dim", parts.join("  ")), 0, 0));
			}
		}

		return c;
	},
});

// =============================================================================
// Extension entry point
// =============================================================================

export default function (pi: ExtensionAPI) {
	pi.registerTool(askClaudeTool);

	// ── /review-plan ───────────────────────────────────────────────────────

	pi.registerCommand("review-plan", {
		description: "Editor → Claude Opus plan_review → inject review into conversation",
		handler: async (_args, ctx) => {
			const input = await ctx.ui.editor(
				"Plan Review · Claude Opus",
				"Paste your plan or strategy. Claude will review for correctness, completeness, and risk.",
			);
			if (!input?.trim()) { ctx.ui.notify("Cancelled.", "info"); return; }
			ctx.ui.setStatus("ask-claude", "Asking Claude Opus (plan_review)…");
			try {
				const r = await runClaude(input, { agent: "plan_review", cwd: ctx.cwd, onUpdate: () => {} });
				if (!r.finalText.trim()) { ctx.ui.notify("No output from Claude.", "warning"); return; }
				pi.sendMessage(
					{ customType: "ask-claude-review", content: r.finalText.trim(), display: true,
					  details: { label: "Claude Opus · plan_review", output: r.finalText } },
					{ triggerTurn: true },
				);
			} catch (err) {
				ctx.ui.notify(`Claude error: ${err instanceof Error ? err.message : String(err)}`, "error");
			} finally { ctx.ui.setStatus("ask-claude", undefined); }
		},
	});

	// ── /review-code ───────────────────────────────────────────────────────

	pi.registerCommand("review-code", {
		description: "Editor → Claude Sonnet code_review → inject review into conversation",
		handler: async (_args, ctx) => {
			const input = await ctx.ui.editor(
				"Code Review · Claude Sonnet",
				"Paste code to review. Include the plan it implements and any specific concerns.",
			);
			if (!input?.trim()) { ctx.ui.notify("Cancelled.", "info"); return; }
			ctx.ui.setStatus("ask-claude", "Asking Claude Sonnet (code_review)…");
			try {
				const r = await runClaude(input, { agent: "code_review", cwd: ctx.cwd, onUpdate: () => {} });
				if (!r.finalText.trim()) { ctx.ui.notify("No output from Claude.", "warning"); return; }
				pi.sendMessage(
					{ customType: "ask-claude-review", content: r.finalText.trim(), display: true,
					  details: { label: "Claude Sonnet · code_review", output: r.finalText } },
					{ triggerTurn: true },
				);
			} catch (err) {
				ctx.ui.notify(`Claude error: ${err instanceof Error ? err.message : String(err)}`, "error");
			} finally { ctx.ui.setStatus("ask-claude", undefined); }
		},
	});

	// ── Message renderer for injected reviews ──────────────────────────────

	pi.registerMessageRenderer("ask-claude-review", (message, { expanded }, theme) => {
		const d = message.details as { label?: string; output?: string } | undefined;
		const label = d?.label ?? "Claude";
		const output = (d?.output ?? "").trim();

		if (expanded) {
			const c = new Container();
			c.addChild(new Text(theme.fg("accent", "◆ ") + theme.fg("toolTitle", theme.bold(label)), 0, 0));
			c.addChild(new Spacer(1));
			c.addChild(new Markdown(output, 0, 0, getMarkdownTheme()));
			return c;
		}

		let text = theme.fg("accent", "◆ ") + theme.fg("toolTitle", theme.bold(label));
		const lines = output.split("\n").filter((l) => l.trim());
		const preview = lines.slice(0, 4).join("\n");
		if (preview) {
			text += "\n" + theme.fg("dim", preview);
			if (lines.length > 4) text += "\n" + theme.fg("muted", `… ${lines.length - 4} more (Ctrl+O)`);
		}
		return new Text(text, 0, 0);
	});
}
