import { Agent, NODE, ANIM } from "@/lib/agent-types";
import type { PauseReason } from "../../../orchviz/protocol";
import { COLORS, getStateColor, contextSegments } from "@/lib/colors";
import { AGENT_DRAW, CONTEXT_BAR, CONTEXT_RING, STATS_OVERLAY } from "@/lib/canvas-constants";
import { alphaHex, formatTokens } from "@/lib/utils";
import { truncateText, drawHexagon, CLAUDE_SPARK_D, OPENAI_LOGO_D, OPENAI_LOGO_VIEWBOX } from "./draw-misc";
import { getAgentGlowSprite } from "./render-cache";
import {
	drawLockIcon,
	drawQuestionBubbleIcon,
	drawDocumentCheckIcon,
	drawForbiddenIcon,
	drawShieldBarIcon,
	drawCornerBadge,
} from "./pause-icons";

let _claudeSparkPath: Path2D | null = null;
export function getClaudeSparkPath() {
	if (!_claudeSparkPath) _claudeSparkPath = new Path2D(CLAUDE_SPARK_D);
	return _claudeSparkPath;
}

let _openaiLogoPath: Path2D | null = null;
function getOpenAILogoPath() {
	if (!_openaiLogoPath) _openaiLogoPath = new Path2D(OPENAI_LOGO_D);
	return _openaiLogoPath;
}

export function drawClaudeSpark(ctx: CanvasRenderingContext2D, cx: number, cy: number, r: number, color: string) {
	ctx.save();
	ctx.translate(cx, cy);
	const scale = (r * AGENT_DRAW.sparkScale) / AGENT_DRAW.sparkViewBox;
	ctx.scale(scale, scale);
	ctx.translate(-AGENT_DRAW.sparkViewBox, -AGENT_DRAW.sparkViewBox + 1);
	ctx.fillStyle = color;
	ctx.shadowColor = color;
	ctx.shadowBlur = 6 / scale;
	ctx.fill(getClaudeSparkPath());
	ctx.restore();
}

export function drawOpenAILogo(ctx: CanvasRenderingContext2D, cx: number, cy: number, r: number, color: string) {
	ctx.save();
	ctx.translate(cx, cy);
	// Target diameter matches the Claude spark: (r * sparkScale) total.
	const scale = (r * AGENT_DRAW.sparkScale) / OPENAI_LOGO_VIEWBOX;
	ctx.scale(scale, scale);
	ctx.translate(-OPENAI_LOGO_VIEWBOX / 2, -OPENAI_LOGO_VIEWBOX / 2);
	ctx.fillStyle = color;
	ctx.shadowColor = color;
	ctx.shadowBlur = 6 / scale;
	ctx.fill(getOpenAILogoPath());
	ctx.restore();
}

/** Pick the brand logo for the agent's runtime. Defaults to Claude. */
export function drawAgentBrand(
	ctx: CanvasRenderingContext2D,
	cx: number,
	cy: number,
	r: number,
	color: string,
	runtime: Agent["runtime"],
) {
	if (runtime === "codex") drawOpenAILogo(ctx, cx, cy, r, color);
	else drawClaudeSpark(ctx, cx, cy, r, color);
}

export function drawContextComposition(ctx: CanvasRenderingContext2D, agent: Agent, radius: number) {
	const bd = agent.contextBreakdown;
	const total = agent.tokensUsed;
	if (total <= 0) return;

	const barWidth = Math.max(CONTEXT_BAR.minWidth, radius * CONTEXT_BAR.widthMultiplier);
	const barHeight = CONTEXT_BAR.barHeight;
	const barX = agent.x - barWidth / 2;
	const barY = agent.y + radius + CONTEXT_BAR.yOffset;

	// Background
	ctx.fillStyle = COLORS.cardBgDark;
	ctx.beginPath();
	ctx.roundRect(barX - 2, barY - 2, barWidth + 4, barHeight + 14, CONTEXT_BAR.borderRadius);
	ctx.fill();

	// Label
	ctx.fillStyle = COLORS.textMuted;
	ctx.font = `${CONTEXT_BAR.fontSize}px monospace`;
	ctx.textAlign = "center";
	ctx.fillText(
		`${formatTokens(total)} / ${formatTokens(agent.tokensMax)} tokens`,
		agent.x,
		barY + barHeight + CONTEXT_BAR.labelPadding,
	);

	// Segments
	const segments = contextSegments(bd);

	let x = barX;
	const maxWidth = barWidth * (total / agent.tokensMax);

	for (const seg of segments) {
		if (seg.value <= 0) continue;
		const segWidth = (seg.value / total) * maxWidth;
		ctx.fillStyle = seg.color;
		ctx.fillRect(x, barY, segWidth, barHeight);
		x += segWidth;
	}

	// Remaining capacity
	if (x < barX + barWidth) {
		ctx.fillStyle = COLORS.holoBg05;
		ctx.fillRect(x, barY, barX + barWidth - x, barHeight);
	}

	ctx.strokeStyle = COLORS.glassBorder;
	ctx.lineWidth = 0.5;
	ctx.strokeRect(barX, barY, barWidth, barHeight);
}

export function drawContextRing(ctx: CanvasRenderingContext2D, agent: Agent, radius: number, time: number) {
	const bd = agent.contextBreakdown;
	const total = agent.tokensUsed;
	if (total <= 0) return;

	const usage = total / agent.tokensMax;
	const ringR = radius + CONTEXT_RING.ringOffset;
	const ringW = CONTEXT_RING.ringWidth;
	const startAngle = -Math.PI / 2;

	// Background ring (empty capacity)
	ctx.beginPath();
	ctx.arc(agent.x, agent.y, ringR, 0, Math.PI * 2);
	ctx.strokeStyle = COLORS.holoBorder06;
	ctx.lineWidth = ringW;
	ctx.stroke();

	// Filled segments
	const segments = contextSegments(bd);

	let currentAngle = startAngle;
	for (const seg of segments) {
		if (seg.value <= 0) continue;
		const sweep = (seg.value / agent.tokensMax) * Math.PI * 2;
		ctx.beginPath();
		ctx.arc(agent.x, agent.y, ringR, currentAngle, currentAngle + sweep);
		ctx.strokeStyle = seg.color;
		ctx.lineWidth = ringW;
		ctx.stroke();
		currentAngle += sweep;
	}

	// Warning glow at high usage
	if (usage > CONTEXT_RING.warningThreshold) {
		const warningColor = usage > CONTEXT_RING.criticalThreshold ? COLORS.error : COLORS.tool;
		const intensity =
			usage > CONTEXT_RING.criticalThreshold ? 0.35 + Math.sin(time * 6) * 0.2 : 0.15 + Math.sin(time * 3) * 0.1;

		ctx.save();
		ctx.beginPath();
		ctx.arc(agent.x, agent.y, ringR + CONTEXT_RING.glowPadding, 0, Math.PI * 2);
		ctx.strokeStyle = warningColor;
		ctx.lineWidth = CONTEXT_RING.glowLineWidth;
		ctx.globalAlpha = intensity;
		ctx.shadowColor = warningColor;
		ctx.shadowBlur = CONTEXT_RING.glowBlur;
		ctx.stroke();
		ctx.restore();
	}

	// Percentage label when usage is high
	if (usage > CONTEXT_RING.percentLabelThreshold) {
		ctx.font = `${CONTEXT_BAR.fontSize}px monospace`;
		ctx.textAlign = "center";
		ctx.textBaseline = "bottom";
		ctx.fillStyle =
			usage > CONTEXT_RING.criticalThreshold
				? COLORS.error
				: usage > CONTEXT_RING.warningThreshold
					? COLORS.tool
					: COLORS.textDim;
		ctx.fillText(`${Math.floor(usage * 100)}%`, agent.x, agent.y - radius - CONTEXT_RING.percentYOffset);
	}
}

function drawDepthShadow(ctx: CanvasRenderingContext2D, agent: Agent, r: number) {
	ctx.save();
	ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
	ctx.shadowBlur = AGENT_DRAW.shadowBlur;
	ctx.shadowOffsetX = AGENT_DRAW.shadowOffsetX;
	ctx.shadowOffsetY = AGENT_DRAW.shadowOffsetY;
	drawHexagon(ctx, agent.x, agent.y, r * 0.9);
	ctx.fillStyle = COLORS.cardBgFaintOverlay;
	ctx.fill();
	ctx.restore();
}

function drawAgentGlow(
	ctx: CanvasRenderingContext2D,
	agent: Agent,
	r: number,
	color: string,
	isHovered: boolean,
	isSelected: boolean,
	isPaused: boolean,
) {
	const glowR = r + AGENT_DRAW.glowPadding;
	const glowAlpha = isHovered || isSelected ? 0.35 : isPaused ? 0.3 : agent.state === "thinking" ? 0.2 : 0.1;
	// Pre-rendered glow sprite instead of per-frame gradient creation
	const sprite = getAgentGlowSprite(color, Math.round(r * 0.5), Math.ceil(glowR), alphaHex(glowAlpha));
	ctx.drawImage(sprite, agent.x - Math.ceil(glowR), agent.y - Math.ceil(glowR));

	// Ambient outer hex ring
	drawHexagon(ctx, agent.x, agent.y, r + AGENT_DRAW.outerRingOffset);
	ctx.strokeStyle = color + "25";
	ctx.lineWidth = 1;
	ctx.stroke();

	// Inner hex fill
	drawHexagon(ctx, agent.x, agent.y, r);
	ctx.fillStyle = COLORS.nodeInterior;
	ctx.fill();
}

function drawScanline(
	ctx: CanvasRenderingContext2D,
	agent: Agent,
	r: number,
	color: string,
	isHovered: boolean,
	isPaused: boolean,
	time: number,
) {
	const scanSpeed = agent.state === "thinking" || isHovered || isPaused ? ANIM.scanline.thinking : ANIM.scanline.normal;
	const scanY = agent.y - r + ((time * scanSpeed) % (r * 2));
	ctx.save();
	drawHexagon(ctx, agent.x, agent.y, r);
	ctx.clip();
	const scanGrad = ctx.createLinearGradient(
		agent.x,
		scanY - AGENT_DRAW.scanlineHalfH,
		agent.x,
		scanY + AGENT_DRAW.scanlineHalfH,
	);
	const scanAlpha = isHovered ? "35" : "20";
	scanGrad.addColorStop(0, color + "00");
	scanGrad.addColorStop(0.5, color + scanAlpha);
	scanGrad.addColorStop(1, color + "00");
	ctx.fillStyle = scanGrad;
	ctx.fillRect(agent.x - r, scanY - AGENT_DRAW.scanlineHalfH, r * 2, AGENT_DRAW.scanlineWidth);
	ctx.restore();
}

function drawStateRing(
	ctx: CanvasRenderingContext2D,
	agent: Agent,
	r: number,
	color: string,
	isHovered: boolean,
	isSelected: boolean,
	isPaused: boolean,
	time: number,
) {
	drawHexagon(ctx, agent.x, agent.y, r);
	ctx.strokeStyle = color;
	ctx.lineWidth = isSelected || isHovered ? 2.5 : 2;
	if (agent.state === "complete") {
		ctx.setLineDash([4, 4]);
		ctx.strokeStyle = color + "60";
	} else if (isPaused) {
		ctx.setLineDash([6, 4]);
		ctx.lineDashOffset = -time * AGENT_DRAW.waitingDashSpeed;
		ctx.lineWidth = 2.5;
	}
	ctx.stroke();
	ctx.setLineDash([]);
	ctx.lineDashOffset = 0;
}

// [red-team #2] Lock-icon drawing extracted to pause-icons.ts:drawLockIcon.
// Per-reason switch added for phase-03 typed pause reasons.
function drawCenterIcon(ctx: CanvasRenderingContext2D, agent: Agent, r: number, color: string, isPaused: boolean) {
	if (isPaused) {
		// Covers both state === "paused" and legacy state === "waiting_permission"
		const reason: PauseReason = agent.pauseReason ?? "permission_request";
		switch (reason) {
			case "permission_request":
				drawLockIcon(ctx, agent.x, agent.y, r, color);
				break;
			case "ask_user_question":
				drawQuestionBubbleIcon(ctx, agent.x, agent.y, r, color);
				break;
			case "plan_mode_review":
				drawDocumentCheckIcon(ctx, agent.x, agent.y, r, color);
				break;
			case "tool_rejected":
				drawForbiddenIcon(ctx, agent.x, agent.y, r, color);
				break;
			case "hook_blocked":
				drawShieldBarIcon(ctx, agent.x, agent.y, r, color);
				break;
		}
	} else if (agent.isMain) {
		drawAgentBrand(ctx, agent.x, agent.y, r, color + "90", agent.runtime);
	} else {
		ctx.fillStyle = color + "90";
		ctx.font = `${r * AGENT_DRAW.subIconScale}px monospace`;
		ctx.textAlign = "center";
		ctx.textBaseline = "middle";
		ctx.fillText(agent.state === "tool_calling" ? "\u2699" : "\u25C7", agent.x, agent.y);
	}
}

function drawOrbitingParticles(ctx: CanvasRenderingContext2D, agent: Agent, r: number, color: string, time: number) {
	for (let i = 0; i < 4; i++) {
		const angle = time * ANIM.orbitSpeed + (i / 4) * Math.PI * 2;
		ctx.beginPath();
		ctx.fillStyle = color + "80";
		ctx.arc(
			agent.x + Math.cos(angle) * (r + AGENT_DRAW.orbitParticleOffset),
			agent.y + Math.sin(angle) * (r + AGENT_DRAW.orbitParticleOffset),
			AGENT_DRAW.orbitParticleSize,
			0,
			Math.PI * 2,
		);
		ctx.fill();
	}
}

function drawWaitingRipples(ctx: CanvasRenderingContext2D, agent: Agent, r: number, color: string, time: number) {
	// Radar ripples — 2 concentric rings expanding outward, staggered
	for (let i = 0; i < 2; i++) {
		const ripplePhase = (time * 0.65 + i * 0.5) % 1.0;
		const rippleR = r + AGENT_DRAW.rippleInnerOffset + ripplePhase * AGENT_DRAW.rippleMaxExpand;
		const rippleAlpha = (1 - ripplePhase) * AGENT_DRAW.rippleMaxAlpha;
		ctx.beginPath();
		drawHexagon(ctx, agent.x, agent.y, rippleR);
		ctx.strokeStyle = color + alphaHex(rippleAlpha);
		ctx.lineWidth = 1.5 * (1 - ripplePhase);
		ctx.stroke();
	}

	// Slower orbiting particles in amber
	for (let i = 0; i < 3; i++) {
		const angle = time * AGENT_DRAW.waitingOrbitSpeed + (i / 3) * Math.PI * 2;
		ctx.beginPath();
		ctx.fillStyle = color + "70";
		ctx.arc(
			agent.x + Math.cos(angle) * (r + AGENT_DRAW.waitingOrbitOffset),
			agent.y + Math.sin(angle) * (r + AGENT_DRAW.waitingOrbitOffset),
			AGENT_DRAW.waitingOrbitParticleSize,
			0,
			Math.PI * 2,
		);
		ctx.fill();
	}
}

function drawAgentLabel(ctx: CanvasRenderingContext2D, agent: Agent, r: number, isHovered: boolean) {
	ctx.fillStyle = isHovered ? COLORS.textPrimary : COLORS.textDim;
	ctx.font = "10px monospace";
	ctx.textAlign = "center";
	ctx.textBaseline = "top";
	const maxLabelW = r * AGENT_DRAW.labelWidthMultiplier;
	const agentLabel = truncateText(ctx, agent.name, maxLabelW);
	ctx.fillText(agentLabel, agent.x, agent.y + r + AGENT_DRAW.labelYOffset);
}

function drawStatsOverlay(ctx: CanvasRenderingContext2D, agent: Agent, r: number) {
	const sy = agent.y - r - STATS_OVERLAY.yOffset;
	ctx.fillStyle = COLORS.cardBgDark;
	ctx.beginPath();
	ctx.roundRect(
		agent.x - STATS_OVERLAY.boxWidth / 2,
		sy,
		STATS_OVERLAY.boxWidth,
		STATS_OVERLAY.boxHeight,
		STATS_OVERLAY.borderRadius,
	);
	ctx.fill();
	ctx.strokeStyle = COLORS.glassBorder;
	ctx.lineWidth = 0.5;
	ctx.stroke();
	ctx.fillStyle = COLORS.textMuted;
	ctx.font = `${STATS_OVERLAY.fontSize}px monospace`;
	ctx.textAlign = "center";
	ctx.textBaseline = "top";
	ctx.fillText(
		`${agent.toolCalls} tools \u00B7 ${agent.timeAlive.toFixed(1)}s`,
		agent.x,
		sy + STATS_OVERLAY.textPaddingY,
	);
}

export function drawAgents(
	ctx: CanvasRenderingContext2D,
	agents: Map<string, Agent>,
	selectedAgentId: string | null,
	hoveredAgentId: string | null,
	showStats: boolean,
	time: number,
) {
	for (const [id, agent] of agents) {
		const radius = agent.isMain ? NODE.radiusMain : NODE.radiusSub;
		const color = getStateColor(agent.state);
		const isHovered = id === hoveredAgentId;
		const isSelected = id === selectedAgentId;

		// isPaused covers both typed "paused" state and legacy "waiting_permission"
		const isPaused = agent.state === "paused" || agent.state === "waiting_permission";
		const reason: PauseReason = agent.pauseReason ?? "permission_request";

		const breathe = isPaused
			? Math.sin(time * AGENT_DRAW.waitingBreatheSpeed) * AGENT_DRAW.waitingBreatheAmp + 1
			: agent.state === "thinking"
				? Math.sin(time * ANIM.breathe.thinkingSpeed) * ANIM.breathe.thinkingAmp + 1
				: agent.state === "idle"
					? Math.sin(time * ANIM.breathe.idleSpeed) * ANIM.breathe.idleAmp + 1
					: 1;

		const r = radius * breathe * agent.scale;

		ctx.save();
		ctx.globalAlpha = agent.opacity;

		drawDepthShadow(ctx, agent, r);
		drawAgentGlow(ctx, agent, r, color, isHovered, isSelected, isPaused);
		drawScanline(ctx, agent, r, color, isHovered, isPaused, time);
		drawStateRing(ctx, agent, r, color, isHovered, isSelected, isPaused, time);
		drawCenterIcon(ctx, agent, r, color, isPaused);

		if (agent.state === "thinking") {
			drawOrbitingParticles(ctx, agent, r, color, time);
		}

		if (isPaused) {
			drawWaitingRipples(ctx, agent, r, color, time);
		}

		// Corner badge for ask_user_question (option count) and hook_blocked ("!")
		if (isPaused) {
			if (reason === "ask_user_question") {
				const optionCount = agent.pauseDetail?.questions?.[0]?.options?.length ?? 0;
				if (optionCount > 0) {
					drawCornerBadge(ctx, agent.x, agent.y, r, String(optionCount), color);
				}
			} else if (reason === "hook_blocked") {
				drawCornerBadge(ctx, agent.x, agent.y, r, "!", color);
			}
		}

		drawAgentLabel(ctx, agent, r, isHovered);

		// Context composition — ring for main agent, bar for sub-agents
		if (agent.state !== "complete" || agent.opacity > 0.5) {
			if (agent.isMain) {
				drawContextRing(ctx, agent, r, time);
			}
			drawContextComposition(ctx, agent, r);
		}

		if (showStats && agent.state !== "complete") {
			drawStatsOverlay(ctx, agent, r);
		}

		ctx.restore();
	}
}
