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 {
	TestResultsResponse,
	TokenCostResponse,
} from "../api/client";
import { api } from "../api/client";
import { AgentLogsSection } from "./AgentLogsSection";
import { TestResultsSection } from "./TestResultsSection";
import { TokenCostSection } from "./TokenCostSection";
import { WorkItemDetailPanelHeader } from "./WorkItemDetailPanelHeader";
import { stripDisplayContent } from "./workItemDetailPanelUtils";

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

/** Parse and format an origin JSON string for display. */
function formatOrigin(origin: string | null): string {
	if (!origin) return "unknown";
	try {
		const obj = JSON.parse(origin) as {
			kind?: string;
			id?: string;
			ts?: number;
		};
		const kind = obj.kind ?? "unknown";
		const id = obj.id ? ` (${obj.id})` : "";
		const ts = obj.ts
			? ` at ${new Date(obj.ts * 1000).toISOString().replace("T", " ").slice(0, 19)}Z`
			: "";
		return `${kind}${id}${ts}`;
	} catch {
		return origin;
	}
}

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

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 [origin, setOrigin] = 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);
				setOrigin(data.origin);
			})
			.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);
			});
	}, []);

	// 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],
	);

	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",
			}}
		>
			<WorkItemDetailPanelHeader
				storyId={storyId}
				name={name}
				stage={stage}
				assignedAgent={assignedAgent}
				agentConfig={agentConfig}
				agentInfo={agentInfo}
				agentStatus={agentStatus}
				assigning={assigning}
				assignError={assignError}
				onAgentAssign={handleAgentAssign}
				onClose={onClose}
			/>

			{/* 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>
								),
							}}
						>
							{stripDisplayContent(content)}
						</Markdown>
					</div>
				)}

				<TokenCostSection tokenCost={tokenCost} />

				<TestResultsSection testResults={testResults} />

				{!loading && (
					<div
						data-testid="detail-panel-origin"
						style={{
							fontSize: "0.75em",
							color: "#555",
							fontFamily: "monospace",
						}}
					>
						origin: {formatOrigin(origin)}
					</div>
				)}

				<div
					style={{
						display: "flex",
						flexDirection: "column",
						gap: "8px",
					}}
				>
					<AgentLogsSection
						agentInfo={agentInfo}
						agentStatus={agentStatus}
						agentLog={agentLog}
					/>

					{/* 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>
	);
}
