import * as React from "react";
import type {
	AgentConfigInfo,
	AgentEvent,
	AgentInfo,
	AgentStatusValue,
} from "../api/agents";
import { agentsApi, subscribeAgentStream } from "../api/agents";
import { settingsApi } from "../api/settings";
import type { UpcomingStory } from "../api/workflow";

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

interface AgentPanelProps {
	stories: UpcomingStory[];
}

interface AgentState {
	agentName: string;
	status: AgentStatusValue;
	log: string[];
	sessionId: string | null;
	worktreePath: string | null;
	baseBranch: string | null;
}

const STATUS_COLORS: Record<AgentStatusValue, string> = {
	pending: "#e3b341",
	running: "#58a6ff",
	completed: "#7ee787",
	failed: "#ff7b72",
};

const STATUS_LABELS: Record<AgentStatusValue, string> = {
	pending: "Pending",
	running: "Running",
	completed: "Completed",
	failed: "Failed",
};

const formatTimestamp = (value: Date | null): string => {
	if (!value) return "";
	return value.toLocaleTimeString([], {
		hour: "2-digit",
		minute: "2-digit",
		second: "2-digit",
	});
};

function StatusBadge({ status }: { status: AgentStatusValue }) {
	return (
		<span
			style={{
				display: "inline-flex",
				alignItems: "center",
				gap: "4px",
				padding: "2px 8px",
				borderRadius: "999px",
				fontSize: "0.75em",
				fontWeight: 600,
				background: `${STATUS_COLORS[status]}22`,
				color: STATUS_COLORS[status],
				border: `1px solid ${STATUS_COLORS[status]}44`,
			}}
		>
			{status === "running" && (
				<span
					style={{
						width: "6px",
						height: "6px",
						borderRadius: "50%",
						background: STATUS_COLORS[status],
						animation: "pulse 1.5s infinite",
					}}
				/>
			)}
			{STATUS_LABELS[status]}
		</span>
	);
}

function RosterBadge({ agent }: { agent: AgentConfigInfo }) {
	return (
		<span
			style={{
				display: "inline-flex",
				alignItems: "center",
				gap: "4px",
				padding: "2px 8px",
				borderRadius: "6px",
				fontSize: "0.7em",
				background: "#ffffff08",
				color: "#888",
				border: "1px solid #333",
			}}
			title={agent.role || agent.name}
		>
			<span style={{ fontWeight: 600, color: "#aaa" }}>{agent.name}</span>
			{agent.model && <span style={{ color: "#666" }}>{agent.model}</span>}
		</span>
	);
}

/** Build a composite key for tracking agent state. */
function agentKey(storyId: string, agentName: string): string {
	return `${storyId}:${agentName}`;
}

function DiffCommand({
	worktreePath,
	baseBranch,
}: {
	worktreePath: string;
	baseBranch: string;
}) {
	const [copied, setCopied] = useState(false);
	const command = `cd "${worktreePath}" && git difftool ${baseBranch}...HEAD`;

	const handleCopy = async () => {
		try {
			await navigator.clipboard.writeText(command);
			setCopied(true);
			setTimeout(() => setCopied(false), 2000);
		} catch {
			// Fallback: select text for manual copy
		}
	};

	return (
		<div
			style={{
				display: "flex",
				alignItems: "center",
				gap: "6px",
				marginBottom: "6px",
			}}
		>
			<code
				style={{
					flex: 1,
					fontSize: "0.7em",
					color: "#8b949e",
					background: "#0d1117",
					padding: "4px 8px",
					borderRadius: "4px",
					border: "1px solid #21262d",
					overflow: "hidden",
					textOverflow: "ellipsis",
					whiteSpace: "nowrap",
				}}
			>
				{command}
			</code>
			<button
				type="button"
				onClick={handleCopy}
				style={{
					padding: "3px 8px",
					borderRadius: "4px",
					border: "1px solid #30363d",
					background: copied ? "#238636" : "#21262d",
					color: copied ? "#fff" : "#8b949e",
					cursor: "pointer",
					fontSize: "0.7em",
					fontWeight: 600,
					whiteSpace: "nowrap",
				}}
			>
				{copied ? "Copied" : "Copy"}
			</button>
		</div>
	);
}

export function EditorCommand({
	worktreePath,
	editorCommand,
}: {
	worktreePath: string;
	editorCommand: string;
}) {
	const [copied, setCopied] = useState(false);
	const command = `${editorCommand} "${worktreePath}"`;

	const handleCopy = async () => {
		try {
			await navigator.clipboard.writeText(command);
			setCopied(true);
			setTimeout(() => setCopied(false), 2000);
		} catch {
			// Fallback: select text for manual copy
		}
	};

	return (
		<div
			style={{
				display: "flex",
				alignItems: "center",
				gap: "6px",
				marginBottom: "6px",
			}}
		>
			<code
				style={{
					flex: 1,
					fontSize: "0.7em",
					color: "#8b949e",
					background: "#0d1117",
					padding: "4px 8px",
					borderRadius: "4px",
					border: "1px solid #21262d",
					overflow: "hidden",
					textOverflow: "ellipsis",
					whiteSpace: "nowrap",
				}}
			>
				{command}
			</code>
			<button
				type="button"
				onClick={handleCopy}
				style={{
					padding: "3px 8px",
					borderRadius: "4px",
					border: "1px solid #30363d",
					background: copied ? "#238636" : "#21262d",
					color: copied ? "#fff" : "#8b949e",
					cursor: "pointer",
					fontSize: "0.7em",
					fontWeight: 600,
					whiteSpace: "nowrap",
				}}
			>
				{copied ? "Copied" : "Open"}
			</button>
		</div>
	);
}

export function AgentPanel({ stories }: AgentPanelProps) {
	const [agents, setAgents] = useState<Record<string, AgentState>>({});
	const [roster, setRoster] = useState<AgentConfigInfo[]>([]);
	const [expandedKey, setExpandedKey] = useState<string | null>(null);
	const [actionError, setActionError] = useState<string | null>(null);
	const [lastRefresh, setLastRefresh] = useState<Date | null>(null);
	const [selectorStory, setSelectorStory] = useState<string | null>(null);
	const [editorCommand, setEditorCommand] = useState<string | null>(null);
	const [editorInput, setEditorInput] = useState<string>("");
	const [editingEditor, setEditingEditor] = useState(false);
	const cleanupRefs = useRef<Record<string, () => void>>({});
	const logEndRefs = useRef<Record<string, HTMLDivElement | null>>({});

	// Load roster, existing agents, and editor preference on mount
	useEffect(() => {
		agentsApi
			.getAgentConfig()
			.then(setRoster)
			.catch((err) => console.error("Failed to load agent config:", err));

		agentsApi
			.listAgents()
			.then((agentList) => {
				const agentMap: Record<string, AgentState> = {};
				for (const a of agentList) {
					const key = agentKey(a.story_id, a.agent_name);
					agentMap[key] = {
						agentName: a.agent_name,
						status: a.status,
						log: [],
						sessionId: a.session_id,
						worktreePath: a.worktree_path,
						baseBranch: a.base_branch,
					};
					if (a.status === "running" || a.status === "pending") {
						subscribeToAgent(a.story_id, a.agent_name);
					}
				}
				setAgents(agentMap);
				setLastRefresh(new Date());
			})
			.catch((err) => console.error("Failed to load agents:", err));

		settingsApi
			.getEditorCommand()
			.then((s) => {
				setEditorCommand(s.editor_command);
				setEditorInput(s.editor_command ?? "");
			})
			.catch((err) => console.error("Failed to load editor command:", err));

		return () => {
			for (const cleanup of Object.values(cleanupRefs.current)) {
				cleanup();
			}
		};
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	const subscribeToAgent = useCallback((storyId: string, agentName: string) => {
		const key = agentKey(storyId, agentName);
		cleanupRefs.current[key]?.();

		const cleanup = subscribeAgentStream(
			storyId,
			agentName,
			(event: AgentEvent) => {
				setAgents((prev) => {
					const current = prev[key] ?? {
						agentName,
						status: "pending" as AgentStatusValue,
						log: [],
						sessionId: null,
						worktreePath: null,
						baseBranch: null,
					};

					switch (event.type) {
						case "status":
							return {
								...prev,
								[key]: {
									...current,
									status: (event.status as AgentStatusValue) ?? current.status,
								},
							};
						case "output":
							return {
								...prev,
								[key]: {
									...current,
									log: [...current.log, event.text ?? ""],
								},
							};
						case "done":
							return {
								...prev,
								[key]: {
									...current,
									status: "completed",
									sessionId: event.session_id ?? current.sessionId,
								},
							};
						case "error":
							return {
								...prev,
								[key]: {
									...current,
									status: "failed",
									log: [
										...current.log,
										`[ERROR] ${event.message ?? "Unknown error"}`,
									],
								},
							};
						default:
							return prev;
					}
				});
			},
			() => {
				// SSE error — agent may not be streaming yet
			},
		);

		cleanupRefs.current[key] = cleanup;
	}, []);

	// Auto-scroll log when expanded
	useEffect(() => {
		if (expandedKey) {
			const el = logEndRefs.current[expandedKey];
			el?.scrollIntoView({ behavior: "smooth" });
		}
	}, [expandedKey, agents]);

	const handleStart = async (storyId: string, agentName?: string) => {
		setActionError(null);
		setSelectorStory(null);
		try {
			const info: AgentInfo = await agentsApi.startAgent(storyId, agentName);
			const key = agentKey(info.story_id, info.agent_name);
			setAgents((prev) => ({
				...prev,
				[key]: {
					agentName: info.agent_name,
					status: info.status,
					log: [],
					sessionId: info.session_id,
					worktreePath: info.worktree_path,
					baseBranch: info.base_branch,
				},
			}));
			setExpandedKey(key);
			subscribeToAgent(info.story_id, info.agent_name);
		} catch (err) {
			const message = err instanceof Error ? err.message : String(err);
			setActionError(`Failed to start agent for ${storyId}: ${message}`);
		}
	};

	const handleStop = async (storyId: string, agentName: string) => {
		setActionError(null);
		const key = agentKey(storyId, agentName);
		try {
			await agentsApi.stopAgent(storyId, agentName);
			cleanupRefs.current[key]?.();
			delete cleanupRefs.current[key];
			setAgents((prev) => {
				const next = { ...prev };
				delete next[key];
				return next;
			});
		} catch (err) {
			const message = err instanceof Error ? err.message : String(err);
			setActionError(`Failed to stop agent for ${storyId}: ${message}`);
		}
	};

	const handleRunClick = (storyId: string) => {
		if (roster.length <= 1) {
			handleStart(storyId);
		} else {
			setSelectorStory(selectorStory === storyId ? null : storyId);
		}
	};

	const handleSaveEditor = async () => {
		try {
			const trimmed = editorInput.trim() || null;
			const result = await settingsApi.setEditorCommand(trimmed);
			setEditorCommand(result.editor_command);
			setEditorInput(result.editor_command ?? "");
			setEditingEditor(false);
		} catch (err) {
			const message = err instanceof Error ? err.message : String(err);
			setActionError(`Failed to save editor: ${message}`);
		}
	};

	/** Get all active agent keys for a story. */
	const getActiveKeysForStory = (storyId: string): string[] => {
		return Object.keys(agents).filter((key) => {
			const a = agents[key];
			return (
				key.startsWith(`${storyId}:`) &&
				(a.status === "running" || a.status === "pending")
			);
		});
	};

	return (
		<div
			style={{
				border: "1px solid #333",
				borderRadius: "10px",
				padding: "12px 16px",
				background: "#1f1f1f",
				display: "flex",
				flexDirection: "column",
				gap: "8px",
			}}
		>
			<div
				style={{
					display: "flex",
					alignItems: "center",
					justifyContent: "space-between",
					gap: "12px",
				}}
			>
				<div
					style={{
						display: "flex",
						alignItems: "center",
						gap: "8px",
					}}
				>
					<div style={{ fontWeight: 600 }}>Agents</div>
					<div
						style={{
							fontSize: "0.75em",
							color: "#777",
							fontFamily: "monospace",
						}}
					>
						{Object.values(agents).filter((a) => a.status === "running").length}{" "}
						running
					</div>
				</div>
				{lastRefresh && (
					<div style={{ fontSize: "0.7em", color: "#555" }}>
						Loaded {formatTimestamp(lastRefresh)}
					</div>
				)}
			</div>

			{/* Editor preference */}
			<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
				<span style={{ fontSize: "0.75em", color: "#666" }}>Editor:</span>
				{editingEditor ? (
					<>
						<input
							type="text"
							value={editorInput}
							onChange={(e) => setEditorInput(e.target.value)}
							onKeyDown={(e) => {
								if (e.key === "Enter") handleSaveEditor();
								if (e.key === "Escape") setEditingEditor(false);
							}}
							placeholder="zed, code, cursor..."
							style={{
								fontSize: "0.75em",
								background: "#111",
								border: "1px solid #444",
								borderRadius: "4px",
								color: "#ccc",
								padding: "2px 6px",
								width: "120px",
							}}
						/>
						<button
							type="button"
							onClick={handleSaveEditor}
							style={{
								fontSize: "0.7em",
								padding: "2px 8px",
								borderRadius: "4px",
								border: "1px solid #238636",
								background: "#238636",
								color: "#fff",
								cursor: "pointer",
							}}
						>
							Save
						</button>
						<button
							type="button"
							onClick={() => setEditingEditor(false)}
							style={{
								fontSize: "0.7em",
								padding: "2px 8px",
								borderRadius: "4px",
								border: "1px solid #444",
								background: "none",
								color: "#888",
								cursor: "pointer",
							}}
						>
							Cancel
						</button>
					</>
				) : (
					<button
						type="button"
						onClick={() => setEditingEditor(true)}
						style={{
							fontSize: "0.75em",
							background: "none",
							border: "1px solid #333",
							borderRadius: "4px",
							color: editorCommand ? "#aaa" : "#555",
							cursor: "pointer",
							padding: "2px 8px",
							fontFamily: editorCommand ? "monospace" : "inherit",
						}}
					>
						{editorCommand ?? "Set editor..."}
					</button>
				)}
			</div>

			{/* Roster badges */}
			{roster.length > 0 && (
				<div
					style={{
						display: "flex",
						flexWrap: "wrap",
						gap: "4px",
					}}
				>
					{roster.map((a) => (
						<RosterBadge key={`roster-${a.name}`} agent={a} />
					))}
				</div>
			)}

			{actionError && (
				<div
					style={{
						fontSize: "0.85em",
						color: "#ff7b72",
						padding: "4px 8px",
						background: "#ff7b7211",
						borderRadius: "6px",
					}}
				>
					{actionError}
				</div>
			)}

			{stories.length === 0 ? (
				<div style={{ fontSize: "0.85em", color: "#aaa" }}>
					No stories available. Add stories to .story_kit/stories/upcoming/.
				</div>
			) : (
				<div
					style={{
						display: "flex",
						flexDirection: "column",
						gap: "6px",
					}}
				>
					{stories.map((story) => {
						const activeKeys = getActiveKeysForStory(story.story_id);
						const hasActive = activeKeys.length > 0;

						// Gather all agent states for this story
						const storyAgentEntries = Object.entries(agents).filter(([key]) =>
							key.startsWith(`${story.story_id}:`),
						);

						return (
							<div
								key={`agent-${story.story_id}`}
								style={{
									border: "1px solid #2a2a2a",
									borderRadius: "8px",
									background: "#191919",
									overflow: "hidden",
								}}
							>
								<div
									style={{
										padding: "8px 12px",
										display: "flex",
										alignItems: "center",
										gap: "8px",
									}}
								>
									<button
										type="button"
										onClick={() => {
											const isExpanded =
												expandedKey?.startsWith(`${story.story_id}:`) ||
												expandedKey === story.story_id;
											setExpandedKey(
												isExpanded
													? null
													: (storyAgentEntries[0]?.[0] ?? story.story_id),
											);
										}}
										onKeyDown={(e) => {
											if (e.key === "Enter" || e.key === " ") {
												const isExpanded =
													expandedKey?.startsWith(`${story.story_id}:`) ||
													expandedKey === story.story_id;
												setExpandedKey(
													isExpanded
														? null
														: (storyAgentEntries[0]?.[0] ?? story.story_id),
												);
											}
										}}
										style={{
											background: "none",
											border: "none",
											color: "#aaa",
											cursor: "pointer",
											fontSize: "0.8em",
											padding: "0 4px",
											transform:
												expandedKey?.startsWith(`${story.story_id}:`) ||
												expandedKey === story.story_id
													? "rotate(90deg)"
													: "rotate(0deg)",
											transition: "transform 0.15s",
										}}
									>
										&#9654;
									</button>

									<div
										style={{
											flex: 1,
											fontWeight: 600,
											fontSize: "0.9em",
											overflow: "hidden",
											textOverflow: "ellipsis",
											whiteSpace: "nowrap",
										}}
									>
										{story.name ?? story.story_id}
									</div>

									{storyAgentEntries.map(([key, a]) => (
										<span
											key={`badge-${key}`}
											style={{
												display: "inline-flex",
												alignItems: "center",
												gap: "4px",
											}}
										>
											<span
												style={{
													fontSize: "0.7em",
													color: "#666",
												}}
											>
												{a.agentName}
											</span>
											<StatusBadge status={a.status} />
										</span>
									))}

									{hasActive ? (
										<button
											type="button"
											onClick={() => {
												for (const key of activeKeys) {
													const a = agents[key];
													if (a) {
														handleStop(story.story_id, a.agentName);
													}
												}
											}}
											style={{
												padding: "4px 10px",
												borderRadius: "999px",
												border: "1px solid #ff7b7244",
												background: "#ff7b7211",
												color: "#ff7b72",
												cursor: "pointer",
												fontSize: "0.75em",
												fontWeight: 600,
											}}
										>
											Stop
										</button>
									) : (
										<div style={{ position: "relative" }}>
											<button
												type="button"
												onClick={() => handleRunClick(story.story_id)}
												style={{
													padding: "4px 10px",
													borderRadius: "999px",
													border: "1px solid #7ee78744",
													background: "#7ee78711",
													color: "#7ee787",
													cursor: "pointer",
													fontSize: "0.75em",
													fontWeight: 600,
												}}
											>
												Run
											</button>
											{selectorStory === story.story_id &&
												roster.length > 1 && (
													<div
														style={{
															position: "absolute",
															top: "100%",
															right: 0,
															marginTop: "4px",
															background: "#222",
															border: "1px solid #444",
															borderRadius: "6px",
															padding: "4px 0",
															zIndex: 10,
															minWidth: "160px",
														}}
													>
														{roster.map((r) => (
															<button
																key={`sel-${r.name}`}
																type="button"
																onClick={() =>
																	handleStart(story.story_id, r.name)
																}
																style={{
																	display: "block",
																	width: "100%",
																	padding: "6px 12px",
																	background: "none",
																	border: "none",
																	color: "#ccc",
																	cursor: "pointer",
																	textAlign: "left",
																	fontSize: "0.8em",
																}}
																onMouseEnter={(e) => {
																	(
																		e.target as HTMLButtonElement
																	).style.background = "#333";
																}}
																onMouseLeave={(e) => {
																	(
																		e.target as HTMLButtonElement
																	).style.background = "none";
																}}
															>
																<div style={{ fontWeight: 600 }}>{r.name}</div>
																{r.role && (
																	<div
																		style={{
																			fontSize: "0.85em",
																			color: "#888",
																		}}
																	>
																		{r.role}
																	</div>
																)}
															</button>
														))}
													</div>
												)}
										</div>
									)}
								</div>

								{/* Empty state when expanded with no agents */}
								{expandedKey === story.story_id &&
									storyAgentEntries.length === 0 && (
										<div
											style={{
												borderTop: "1px solid #2a2a2a",
												padding: "12px",
												fontSize: "0.8em",
												color: "#555",
												textAlign: "center",
											}}
										>
											No agents started. Use the Run button to start an agent.
										</div>
									)}

								{/* Expanded detail per agent */}
								{storyAgentEntries.map(([key, a]) => {
									if (expandedKey !== key) return null;
									return (
										<div
											key={`detail-${key}`}
											style={{
												borderTop: "1px solid #2a2a2a",
												padding: "8px 12px",
											}}
										>
											<div
												style={{
													fontSize: "0.75em",
													color: "#888",
													marginBottom: "4px",
													fontWeight: 600,
												}}
											>
												{a.agentName}
											</div>
											{a.worktreePath && (
												<div
													style={{
														fontSize: "0.75em",
														color: "#666",
														fontFamily: "monospace",
														marginBottom: "6px",
													}}
												>
													Worktree: {a.worktreePath}
												</div>
											)}
											{a.worktreePath && (
												<DiffCommand
													worktreePath={a.worktreePath}
													baseBranch={a.baseBranch ?? "master"}
												/>
											)}
											<div
												style={{
													maxHeight: "300px",
													overflowY: "auto",
													background: "#111",
													borderRadius: "6px",
													padding: "8px",
													fontFamily: "monospace",
													fontSize: "0.8em",
													lineHeight: "1.5",
													color: "#ccc",
													whiteSpace: "pre-wrap",
													wordBreak: "break-word",
												}}
											>
												{a.log.length === 0 ? (
													<span style={{ color: "#555" }}>
														{a.status === "pending" || a.status === "running"
															? "Waiting for output..."
															: "No output captured."}
													</span>
												) : (
													a.log.map((line, i) => (
														<div
															key={`log-${key}-${i.toString()}`}
															style={{
																color: line.startsWith("[ERROR]")
																	? "#ff7b72"
																	: "#ccc",
															}}
														>
															{line}
														</div>
													))
												)}
												<div
													ref={(el) => {
														logEndRefs.current[key] = el;
													}}
												/>
											</div>
										</div>
									);
								})}
							</div>
						);
					})}
				</div>
			)}
		</div>
	);
}
