import * as React from "react";
import Markdown from "react-markdown";
import type {
	AgentConfigInfo,
	AgentEvent,
	AgentInfo,
	AgentStatusValue,
} from "../api/agents";
import { agentsApi, subscribeAgentStream } from "../api/agents";
import type {
	AgentCostEntry,
	TestCaseResult,
	TestResultsResponse,
	TokenCostResponse,
} from "../api/client";
import { api } from "../api/client";

const { useCallback, useEffect, useRef, useState } = React;

const STAGE_LABELS: Record<string, string> = {
	backlog: "Backlog",
	current: "Current",
	qa: "QA",
	merge: "To Merge",
	done: "Done",
	archived: "Archived",
};

const STATUS_COLORS: Record<AgentStatusValue, string> = {
	running: "#3fb950",
	pending: "#e3b341",
	completed: "#aaa",
	failed: "#f85149",
};

interface WorkItemDetailPanelProps {
	storyId: string;
	pipelineVersion: number;
	onClose: () => void;
	/** True when the item is in QA and awaiting human review. */
	reviewHold?: boolean;
}

function TestCaseRow({ tc }: { tc: TestCaseResult }) {
	const isPassing = tc.status === "pass";
	return (
		<div
			data-testid={`test-case-${tc.name}`}
			style={{
				display: "flex",
				flexDirection: "column",
				gap: "2px",
				padding: "4px 0",
			}}
		>
			<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
				<span
					data-testid={`test-status-${tc.name}`}
					style={{
						fontSize: "0.85em",
						color: isPassing ? "#3fb950" : "#f85149",
					}}
				>
					{isPassing ? "PASS" : "FAIL"}
				</span>
				<span style={{ fontSize: "0.82em", color: "#ccc" }}>{tc.name}</span>
			</div>
			{tc.details && (
				<div
					data-testid={`test-details-${tc.name}`}
					style={{
						fontSize: "0.75em",
						color: "#888",
						paddingLeft: "22px",
						whiteSpace: "pre-wrap",
						wordBreak: "break-word",
					}}
				>
					{tc.details}
				</div>
			)}
		</div>
	);
}

function TestSection({
	title,
	tests,
	testId,
}: {
	title: string;
	tests: TestCaseResult[];
	testId: string;
}) {
	const passCount = tests.filter((t) => t.status === "pass").length;
	const failCount = tests.length - passCount;
	return (
		<div data-testid={testId}>
			<div
				style={{
					fontSize: "0.78em",
					fontWeight: 600,
					color: "#aaa",
					marginBottom: "6px",
				}}
			>
				{title} ({passCount} passed, {failCount} failed)
			</div>
			{tests.length === 0 ? (
				<div style={{ fontSize: "0.75em", color: "#555", fontStyle: "italic" }}>
					No tests recorded
				</div>
			) : (
				tests.map((tc) => <TestCaseRow key={tc.name} tc={tc} />)
			)}
		</div>
	);
}

export function WorkItemDetailPanel({
	storyId,
	pipelineVersion,
	onClose,
	reviewHold: _reviewHold,
}: WorkItemDetailPanelProps) {
	const [content, setContent] = useState<string | null>(null);
	const [stage, setStage] = useState<string>("");
	const [name, setName] = useState<string | null>(null);
	const [assignedAgent, setAssignedAgent] = useState<string | null>(null);
	const [loading, setLoading] = useState(true);
	const [error, setError] = useState<string | null>(null);
	const [agentInfo, setAgentInfo] = useState<AgentInfo | null>(null);
	const [agentLog, setAgentLog] = useState<string[]>([]);
	const [agentStatus, setAgentStatus] = useState<AgentStatusValue | null>(null);
	const [testResults, setTestResults] = useState<TestResultsResponse | null>(
		null,
	);
	const [tokenCost, setTokenCost] = useState<TokenCostResponse | null>(null);
	const [agentConfig, setAgentConfig] = useState<AgentConfigInfo[]>([]);
	const [assigning, setAssigning] = useState(false);
	const [assignError, setAssignError] = useState<string | null>(null);
	const panelRef = useRef<HTMLDivElement>(null);
	const cleanupRef = useRef<(() => void) | null>(null);

	useEffect(() => {
		setLoading(true);
		setError(null);
		api
			.getWorkItemContent(storyId)
			.then((data) => {
				setContent(data.content);
				setStage(data.stage);
				setName(data.name);
				setAssignedAgent(data.agent);
			})
			.catch((err: unknown) => {
				setError(err instanceof Error ? err.message : "Failed to load content");
			})
			.finally(() => {
				setLoading(false);
			});
	}, [storyId]);

	// Fetch test results on mount and when pipeline updates arrive.
	useEffect(() => {
		api
			.getTestResults(storyId)
			.then((data) => {
				setTestResults(data);
			})
			.catch(() => {
				// Silently ignore — test results may not exist yet.
			});
	}, [storyId, pipelineVersion]);

	// Fetch token cost on mount and when pipeline updates arrive.
	useEffect(() => {
		api
			.getTokenCost(storyId)
			.then((data) => {
				setTokenCost(data);
			})
			.catch(() => {
				// Silently ignore — token cost may not exist yet.
			});
	}, [storyId, pipelineVersion]);

	useEffect(() => {
		cleanupRef.current?.();
		cleanupRef.current = null;
		setAgentInfo(null);
		setAgentLog([]);
		setAgentStatus(null);

		agentsApi
			.listAgents()
			.then((agents) => {
				const agent = agents.find((a) => a.story_id === storyId);
				if (!agent) return;
				setAgentInfo(agent);
				setAgentStatus(agent.status);

				if (agent.status === "running" || agent.status === "pending") {
					const cleanup = subscribeAgentStream(
						storyId,
						agent.agent_name,
						(event: AgentEvent) => {
							switch (event.type) {
								case "status":
									setAgentStatus((event.status as AgentStatusValue) ?? null);
									break;
								case "output":
									setAgentLog((prev) => [...prev, event.text ?? ""]);
									break;
								case "done":
									setAgentStatus("completed");
									break;
								case "error":
									setAgentStatus("failed");
									setAgentLog((prev) => [
										...prev,
										`[ERROR] ${event.message ?? "Unknown error"}`,
									]);
									break;
								default:
									break;
							}
						},
					);
					cleanupRef.current = cleanup;
				}
			})
			.catch((err: unknown) => {
				console.error("Failed to load agents:", err);
			});

		return () => {
			cleanupRef.current?.();
			cleanupRef.current = null;
		};
	}, [storyId]);

	useEffect(() => {
		const handleKeyDown = (e: KeyboardEvent) => {
			if (e.key === "Escape") {
				onClose();
			}
		};
		window.addEventListener("keydown", handleKeyDown);
		return () => window.removeEventListener("keydown", handleKeyDown);
	}, [onClose]);

	// Load agent config roster for the dropdown.
	useEffect(() => {
		agentsApi
			.getAgentConfig()
			.then((config) => {
				setAgentConfig(config);
			})
			.catch((err: unknown) => {
				console.error("Failed to load agent config:", err);
			});
	}, []);

	// Map pipeline stage → agent stage filter.
	const STAGE_TO_AGENT_STAGE: Record<string, string> = {
		current: "coder",
		qa: "qa",
		merge: "mergemaster",
	};

	const filteredAgents = agentConfig.filter(
		(a) => a.stage === STAGE_TO_AGENT_STAGE[stage],
	);

	// The currently active agent name for this story (running or pending).
	const activeAgentName =
		agentInfo && (agentStatus === "running" || agentStatus === "pending")
			? agentInfo.agent_name
			: null;

	const handleAgentAssign = useCallback(
		async (selectedAgentName: string) => {
			setAssigning(true);
			setAssignError(null);
			try {
				// Stop current running agent if there is one.
				if (activeAgentName) {
					await agentsApi.stopAgent(storyId, activeAgentName);
				}
				// Start the new agent (or skip if "none" selected).
				if (selectedAgentName) {
					await agentsApi.startAgent(storyId, selectedAgentName);
				}
			} catch (err: unknown) {
				setAssignError(
					err instanceof Error ? err.message : "Failed to assign agent",
				);
			} finally {
				setAssigning(false);
			}
		},
		[storyId, activeAgentName],
	);

	const stageLabel = STAGE_LABELS[stage] ?? stage;
	const hasTestResults =
		testResults &&
		(testResults.unit.length > 0 || testResults.integration.length > 0);

	return (
		<div
			data-testid="work-item-detail-panel"
			ref={panelRef}
			style={{
				display: "flex",
				flexDirection: "column",
				height: "100%",
				overflow: "hidden",
				background: "#1a1a1a",
				borderRadius: "8px",
				border: "1px solid #333",
			}}
		>
			{/* Header */}
			<div
				style={{
					display: "flex",
					alignItems: "center",
					justifyContent: "space-between",
					padding: "12px 16px",
					borderBottom: "1px solid #333",
					flexShrink: 0,
				}}
			>
				<div
					style={{
						display: "flex",
						flexDirection: "column",
						gap: "2px",
						minWidth: 0,
					}}
				>
					<div
						data-testid="detail-panel-title"
						style={{
							fontWeight: 600,
							fontSize: "0.95em",
							color: "#ececec",
							overflow: "hidden",
							textOverflow: "ellipsis",
							whiteSpace: "nowrap",
						}}
					>
						{name ?? storyId}
					</div>
					{stage && (
						<div
							data-testid="detail-panel-stage"
							style={{ fontSize: "0.75em", color: "#888" }}
						>
							{stageLabel}
						</div>
					)}
					{filteredAgents.length > 0 && (
						<div
							data-testid="detail-panel-agent-assignment"
							style={{
								display: "flex",
								alignItems: "center",
								gap: "6px",
								marginTop: "4px",
							}}
						>
							<span style={{ fontSize: "0.75em", color: "#666" }}>Agent:</span>
							<select
								data-testid="agent-assignment-dropdown"
								disabled={assigning}
								value={activeAgentName ?? assignedAgent ?? ""}
								onChange={(e) => handleAgentAssign(e.target.value)}
								style={{
									background: "#1a1a1a",
									border: "1px solid #444",
									borderRadius: "4px",
									color: "#ccc",
									cursor: assigning ? "not-allowed" : "pointer",
									fontSize: "0.75em",
									padding: "2px 6px",
									opacity: assigning ? 0.6 : 1,
								}}
							>
								<option value="">— none —</option>
								{filteredAgents.map((a) => {
									const isRunning =
										agentInfo?.agent_name === a.name &&
										agentStatus === "running";
									const isPending =
										agentInfo?.agent_name === a.name &&
										agentStatus === "pending";
									const statusLabel = isRunning
										? " — running"
										: isPending
											? " — pending"
											: " — idle";
									const modelPart = a.model ? ` (${a.model})` : "";
									return (
										<option key={a.name} value={a.name}>
											{a.name}
											{modelPart}
											{statusLabel}
										</option>
									);
								})}
							</select>
							{assigning && (
								<span style={{ fontSize: "0.7em", color: "#888" }}>
									Assigning…
								</span>
							)}
							{assignError && (
								<span
									data-testid="agent-assignment-error"
									style={{ fontSize: "0.7em", color: "#f85149" }}
								>
									{assignError}
								</span>
							)}
						</div>
					)}
					{filteredAgents.length === 0 && assignedAgent ? (
						<div
							data-testid="detail-panel-assigned-agent"
							style={{ fontSize: "0.75em", color: "#888" }}
						>
							Agent: {assignedAgent}
						</div>
					) : null}
				</div>
				<button
					type="button"
					data-testid="detail-panel-close"
					onClick={onClose}
					style={{
						background: "none",
						border: "1px solid #444",
						borderRadius: "6px",
						color: "#aaa",
						cursor: "pointer",
						padding: "4px 10px",
						fontSize: "0.8em",
						flexShrink: 0,
					}}
				>
					Close
				</button>
			</div>

			{/* Scrollable content area */}
			<div
				style={{
					flex: 1,
					overflowY: "auto",
					padding: "16px",
					display: "flex",
					flexDirection: "column",
					gap: "16px",
				}}
			>
				{loading && (
					<div
						data-testid="detail-panel-loading"
						style={{ color: "#666", fontSize: "0.85em" }}
					>
						Loading...
					</div>
				)}
				{error && (
					<div
						data-testid="detail-panel-error"
						style={{ color: "#ff7b72", fontSize: "0.85em" }}
					>
						{error}
					</div>
				)}
				{!loading && !error && content !== null && (
					<div
						data-testid="detail-panel-content"
						className="markdown-body"
						style={{ fontSize: "0.9em", lineHeight: 1.6 }}
					>
						<Markdown
							components={{
								// biome-ignore lint/suspicious/noExplicitAny: react-markdown requires any for component props
								h1: ({ children }: any) => (
									<h1 style={{ fontSize: "1.2em" }}>{children}</h1>
								),
								// biome-ignore lint/suspicious/noExplicitAny: react-markdown requires any for component props
								h2: ({ children }: any) => (
									<h2 style={{ fontSize: "1.1em" }}>{children}</h2>
								),
								// biome-ignore lint/suspicious/noExplicitAny: react-markdown requires any for component props
								h3: ({ children }: any) => (
									<h3 style={{ fontSize: "1em" }}>{children}</h3>
								),
							}}
						>
							{content}
						</Markdown>
					</div>
				)}

				{/* Token Cost section */}
				<div
					data-testid="token-cost-section"
					style={{
						border: "1px solid #2a2a2a",
						borderRadius: "8px",
						padding: "10px 12px",
						background: "#161616",
					}}
				>
					<div
						style={{
							fontWeight: 600,
							fontSize: "0.8em",
							color: "#555",
							marginBottom: "8px",
						}}
					>
						Token Cost
					</div>
					{tokenCost && tokenCost.agents.length > 0 ? (
						<div data-testid="token-cost-content">
							<div
								style={{
									fontSize: "0.75em",
									color: "#888",
									marginBottom: "8px",
								}}
							>
								Total:{" "}
								<span data-testid="token-cost-total" style={{ color: "#ccc" }}>
									${tokenCost.total_cost_usd.toFixed(6)}
								</span>
							</div>
							{tokenCost.agents.map((agent: AgentCostEntry) => (
								<div
									key={agent.agent_name}
									data-testid={`token-cost-agent-${agent.agent_name}`}
									style={{
										fontSize: "0.75em",
										color: "#888",
										padding: "4px 0",
										borderTop: "1px solid #222",
									}}
								>
									<div
										style={{
											display: "flex",
											justifyContent: "space-between",
											marginBottom: "2px",
										}}
									>
										<span style={{ color: "#ccc", fontWeight: 600 }}>
											{agent.agent_name}
											{agent.model ? (
												<span
													style={{ color: "#666", fontWeight: 400 }}
												>{` (${agent.model})`}</span>
											) : null}
										</span>
										<span style={{ color: "#aaa" }}>
											${agent.total_cost_usd.toFixed(6)}
										</span>
									</div>
									<div style={{ color: "#555" }}>
										in {agent.input_tokens.toLocaleString()} / out{" "}
										{agent.output_tokens.toLocaleString()}
										{(agent.cache_creation_input_tokens > 0 ||
											agent.cache_read_input_tokens > 0) && (
											<>
												{" "}
												/ cache +
												{agent.cache_creation_input_tokens.toLocaleString()}{" "}
												read {agent.cache_read_input_tokens.toLocaleString()}
											</>
										)}
									</div>
								</div>
							))}
						</div>
					) : (
						<div
							data-testid="token-cost-empty"
							style={{ fontSize: "0.75em", color: "#444" }}
						>
							No token data recorded
						</div>
					)}
				</div>

				{/* Test Results section */}
				<div
					data-testid="test-results-section"
					style={{
						border: "1px solid #2a2a2a",
						borderRadius: "8px",
						padding: "10px 12px",
						background: "#161616",
					}}
				>
					<div
						style={{
							fontWeight: 600,
							fontSize: "0.8em",
							color: "#555",
							marginBottom: "8px",
						}}
					>
						Test Results
					</div>
					{hasTestResults ? (
						<div
							data-testid="test-results-content"
							style={{
								display: "flex",
								flexDirection: "column",
								gap: "12px",
							}}
						>
							<TestSection
								title="Unit Tests"
								tests={testResults.unit}
								testId="test-section-unit"
							/>
							<TestSection
								title="Integration Tests"
								tests={testResults.integration}
								testId="test-section-integration"
							/>
						</div>
					) : (
						<div
							data-testid="test-results-empty"
							style={{ fontSize: "0.75em", color: "#444" }}
						>
							No test results recorded
						</div>
					)}
				</div>

				<div
					style={{
						display: "flex",
						flexDirection: "column",
						gap: "8px",
					}}
				>
					{/* Agent Logs section */}
					{!agentInfo && (
						<div
							data-testid="placeholder-agent-logs"
							style={{
								border: "1px solid #2a2a2a",
								borderRadius: "8px",
								padding: "10px 12px",
								background: "#161616",
							}}
						>
							<div
								style={{
									fontWeight: 600,
									fontSize: "0.8em",
									color: "#555",
									marginBottom: "4px",
								}}
							>
								Agent Logs
							</div>
							<div style={{ fontSize: "0.75em", color: "#444" }}>
								Coming soon
							</div>
						</div>
					)}
					{agentInfo && (
						<div
							data-testid="agent-logs-section"
							style={{
								border: "1px solid #2a2a2a",
								borderRadius: "8px",
								padding: "10px 12px",
								background: "#161616",
							}}
						>
							<div
								style={{
									display: "flex",
									alignItems: "center",
									justifyContent: "space-between",
									marginBottom: "6px",
								}}
							>
								<div
									style={{
										fontWeight: 600,
										fontSize: "0.8em",
										color: "#888",
									}}
								>
									Agent Logs
								</div>
								{agentStatus && (
									<div
										data-testid="agent-status-badge"
										style={{
											fontSize: "0.7em",
											color: STATUS_COLORS[agentStatus],
											fontWeight: 600,
										}}
									>
										{agentInfo.agent_name} — {agentStatus}
									</div>
								)}
							</div>
							{agentLog.length > 0 ? (
								<div
									data-testid="agent-log-output"
									style={{
										fontSize: "0.75em",
										fontFamily: "monospace",
										color: "#ccc",
										whiteSpace: "pre-wrap",
										wordBreak: "break-word",
										lineHeight: "1.5",
										maxHeight: "200px",
										overflowY: "auto",
									}}
								>
									{agentLog.join("")}
								</div>
							) : (
								<div style={{ fontSize: "0.75em", color: "#444" }}>
									{agentStatus === "running" || agentStatus === "pending"
										? "Waiting for output..."
										: "No output."}
								</div>
							)}
						</div>
					)}

					{/* Placeholder sections for future content */}
					{(
						[{ id: "coverage", label: "Coverage" }] as {
							id: string;
							label: string;
						}[]
					).map(({ id, label }) => (
						<div
							key={id}
							data-testid={`placeholder-${id}`}
							style={{
								border: "1px solid #2a2a2a",
								borderRadius: "8px",
								padding: "10px 12px",
								background: "#161616",
							}}
						>
							<div
								style={{
									fontWeight: 600,
									fontSize: "0.8em",
									color: "#555",
									marginBottom: "4px",
								}}
							>
								{label}
							</div>
							<div style={{ fontSize: "0.75em", color: "#444" }}>
								Coming soon
							</div>
						</div>
					))}
				</div>
			</div>
		</div>
	);
}
