import type { ExportedMessageRepository } from "@assistant-ui/core";
import { ThreadListPrimitive } from "@assistant-ui/react";
import {
	BarChart3Icon,
	BotIcon,
	ChevronDownIcon,
	Clock3Icon,
	LogOutIcon,
	PanelLeftOpenIcon,
	SearchIcon,
	SquarePenIcon,
	UserIcon,
} from "lucide-react";
import { Popover as PopoverPrimitive } from "radix-ui";
import type * as React from "react";
import { useEffect, useState } from "react";
import type { ClawAccount } from "@/app/claw-account-gate";
import { ClawLlmSettings } from "@/app/claw-llm-settings";
import { ClawThemeSwitcher } from "@/app/claw-theme-switcher";
import {
	fetchLatestRunStatus,
	ThreadList,
	toReplayRepository,
} from "@/components/assistant-ui/thread-list";
import { Button } from "@/components/ui/button";
import {
	Dialog,
	DialogContent,
	DialogDescription,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import {
	Sidebar,
	SidebarContent,
	SidebarFooter,
	SidebarHeader,
	SidebarMenu,
	SidebarMenuButton,
	SidebarMenuItem,
	SidebarRail,
	SidebarTrigger,
	useSidebar,
} from "@/components/ui/sidebar";
import { useClawSessionReplay } from "@/lib/claw-session-replay";

type ThreadListSidebarProps = React.ComponentProps<typeof Sidebar> & {
	account: ClawAccount;
	onLogout: () => void;
};

type ClawState = {
	model?: string;
	session_directory?: string;
	active_session_id?: string | null;
};

type ClawSessionSummary = {
	session_id: string;
	turns: number;
	tool_calls: number;
	modified_at: number;
	preview?: string;
	model?: string;
	usage?: UsageSummary;
};

type UsageSummary = {
	input_tokens?: number;
	output_tokens?: number;
	total_tokens?: number;
};

type ClawStoredSession = {
	session_id: string;
	messages?: unknown[];
	usage?: UsageSummary;
	tool_calls?: number;
	turns?: number;
	model?: string;
};

type ReplaySessionPayload = Parameters<typeof toReplayRepository>[0];

type SidebarSession = ClawSessionSummary & {
	preview: string;
};

export function ThreadListSidebar({
	account,
	onLogout,
	...props
}: ThreadListSidebarProps) {
	return (
		<Sidebar collapsible="icon" {...props}>
			<div className="flex h-full flex-col group-data-[collapsible=icon]:hidden">
				<SidebarHeader className="aui-sidebar-header mb-2 border-b">
					<div className="aui-sidebar-header-content flex items-center justify-between gap-2">
						<SidebarMenu>
							<SidebarMenuItem>
								<SidebarMenuButton size="lg">
									<div className="aui-sidebar-header-icon-wrapper flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
										<BotIcon className="aui-sidebar-header-icon size-4" />
									</div>
									<div className="aui-sidebar-header-heading mr-6 flex flex-col gap-0.5 leading-none">
										<span className="aui-sidebar-header-title font-semibold">
											ZK Data Agent
										</span>
										<span className="text-muted-foreground text-xs">
											中控数据开发平台
										</span>
									</div>
								</SidebarMenuButton>
							</SidebarMenuItem>
						</SidebarMenu>
						<SidebarTrigger className="size-8 shrink-0" />
					</div>
				</SidebarHeader>
				<SidebarContent className="aui-sidebar-content px-2">
					<ThreadList />
				</SidebarContent>
				<SidebarRail />
				<SidebarFooter className="aui-sidebar-footer border-t">
					<AccountMenu account={account} onLogout={onLogout} />
				</SidebarFooter>
			</div>
			<div className="hidden h-full group-data-[collapsible=icon]:flex">
				<CollapsedSidebarRail account={account} onLogout={onLogout} />
			</div>
		</Sidebar>
	);
}

function CollapsedSidebarRail({
	account,
	onLogout,
}: {
	account: ClawAccount;
	onLogout: () => void;
}) {
	const { setOpen } = useSidebar();

	return (
		<div className="flex h-full w-full flex-col items-center border-r bg-sidebar py-3 text-sidebar-foreground">
			<button
				type="button"
				className="group mb-5 flex size-9 items-center justify-center rounded-lg text-sidebar-foreground transition-colors hover:bg-sidebar-accent"
				aria-label="展开侧栏"
				title="展开侧栏"
				onClick={() => setOpen(true)}
			>
				<BotIcon className="size-5 group-hover:hidden" />
				<PanelLeftOpenIcon className="hidden size-5 group-hover:block" />
			</button>

			<div className="flex flex-col items-center gap-3">
				<div
					onClickCapture={() => {
						window.localStorage.removeItem("claw.activeSessionId");
						window.dispatchEvent(new Event("claw-active-session-cleared"));
					}}
				>
					<ThreadListPrimitive.New asChild>
						<CollapsedIconButton label="新任务">
							<SquarePenIcon className="size-4" />
						</CollapsedIconButton>
					</ThreadListPrimitive.New>
				</div>
				<CollapsedSessionSearch />
				<CollapsedRecentSessions />
			</div>

			<div className="mt-auto">
				<AccountMenu account={account} onLogout={onLogout} compact />
			</div>
		</div>
	);
}

function CollapsedIconButton({
	label,
	children,
	onClick,
}: {
	label: string;
	children: React.ReactNode;
	onClick?: () => void;
}) {
	return (
		<button
			type="button"
			className="flex size-8 items-center justify-center rounded-lg text-sidebar-foreground transition-colors hover:bg-sidebar-accent"
			aria-label={label}
			title={label}
			onClick={onClick}
		>
			{children}
		</button>
	);
}

function CollapsedSessionSearch() {
	const { replaySession } = useClawSessionReplay();
	const [open, setOpen] = useState(false);
	const [query, setQuery] = useState("");
	const { sessions, loadingSessionId, openSession } = useSidebarSessions(open);
	const filteredSessions = sessions.filter((session) => {
		const keyword = query.trim().toLowerCase();
		if (!keyword) return true;
		return (
			session.preview.toLowerCase().includes(keyword) ||
			session.session_id.toLowerCase().includes(keyword)
		);
	});

	return (
		<>
			<CollapsedIconButton label="搜索任务" onClick={() => setOpen(true)}>
				<SearchIcon className="size-4" />
			</CollapsedIconButton>
			<Dialog open={open} onOpenChange={setOpen}>
				<DialogContent className="top-[35%] max-w-lg translate-y-[-35%] gap-3 p-4">
					<DialogHeader>
						<DialogTitle>搜索任务</DialogTitle>
						<DialogDescription>
							按任务标题或会话 ID 搜索，点击后恢复会话。
						</DialogDescription>
					</DialogHeader>
					<Input
						autoFocus
						value={query}
						placeholder="输入关键词..."
						onChange={(event) => setQuery(event.target.value)}
					/>
					<div className="max-h-80 overflow-y-auto">
						{filteredSessions.length ? (
							<div className="flex flex-col gap-1">
								{filteredSessions.map((session) => (
									<SessionResultButton
										key={session.session_id}
										session={session}
										loading={loadingSessionId === session.session_id}
										onClick={async () => {
											await openSession(session.session_id, replaySession);
											setOpen(false);
										}}
									/>
								))}
							</div>
						) : (
							<p className="py-8 text-center text-muted-foreground text-sm">
								没有匹配的任务。
							</p>
						)}
					</div>
				</DialogContent>
			</Dialog>
		</>
	);
}

function CollapsedRecentSessions() {
	const { replaySession } = useClawSessionReplay();
	const [open, setOpen] = useState(false);
	const { sessions, loadingSessionId, openSession } = useSidebarSessions(open);

	return (
		<PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
			<PopoverPrimitive.Trigger asChild>
				<CollapsedIconButton label="最近任务">
					<Clock3Icon className="size-4" />
				</CollapsedIconButton>
			</PopoverPrimitive.Trigger>
			<PopoverPrimitive.Portal>
				<PopoverPrimitive.Content
					side="right"
					align="start"
					sideOffset={10}
					className="z-50 w-72 rounded-xl border bg-popover p-2 text-popover-foreground shadow-lg outline-none"
				>
					<div className="px-2 py-1.5 font-medium text-sm">最近任务</div>
					{sessions.length ? (
						<div className="flex max-h-96 flex-col gap-1 overflow-y-auto">
							{sessions.map((session) => (
								<SessionResultButton
									key={session.session_id}
									session={session}
									loading={loadingSessionId === session.session_id}
									onClick={async () => {
										await openSession(session.session_id, replaySession);
										setOpen(false);
									}}
								/>
							))}
						</div>
					) : (
						<p className="px-2 py-6 text-center text-muted-foreground text-sm">
							暂无最近任务。
						</p>
					)}
				</PopoverPrimitive.Content>
			</PopoverPrimitive.Portal>
		</PopoverPrimitive.Root>
	);
}

function SessionResultButton({
	session,
	loading,
	onClick,
}: {
	session: SidebarSession;
	loading: boolean;
	onClick: () => void;
}) {
	return (
		<button
			type="button"
			className="flex w-full flex-col rounded-lg px-2 py-2 text-left transition-colors hover:bg-muted"
			onClick={onClick}
		>
			<span className="truncate font-medium text-sm">
				{loading ? "回放中..." : session.preview || session.session_id}
			</span>
			<span className="mt-0.5 text-muted-foreground text-xs">
				{session.turns ?? 0} 轮 · {session.tool_calls ?? 0} 次工具
			</span>
		</button>
	);
}

function useSidebarSessions(open: boolean) {
	const [sessions, setSessions] = useState<SidebarSession[]>([]);
	const [loadingSessionId, setLoadingSessionId] = useState<string | null>(null);

	useEffect(() => {
		if (!open) return;
		let cancelled = false;
		fetch("/api/claw/sessions", { cache: "no-store" })
			.then((res) => (res.ok ? res.json() : []))
			.then((payload) => {
				if (cancelled || !Array.isArray(payload)) return;
				setSessions(dedupeSidebarSessions(payload).slice(0, 12));
			})
			.catch(() => {
				if (!cancelled) setSessions([]);
			});
		return () => {
			cancelled = true;
		};
	}, [open]);

	async function openSession(
		sessionId: string,
		replaySession: (
			sessionId: string,
			repository: ExportedMessageRepository,
		) => void,
	) {
		const cleanSessionId = normalizeSessionId(sessionId);
		if (!cleanSessionId) return;
		window.localStorage.setItem("claw.activeSessionId", cleanSessionId);
		setLoadingSessionId(sessionId);
		try {
			const response = await fetch(`/api/claw/sessions/${cleanSessionId}`, {
				cache: "no-store",
			});
			if (!response.ok) return;
			const payload = (await response.json()) as ReplaySessionPayload;
			const runStatus = await fetchLatestRunStatus(cleanSessionId);
			replaySession(
				cleanSessionId,
				toReplayRepository(payload, cleanSessionId, runStatus),
			);
		} finally {
			setLoadingSessionId(null);
		}
	}

	return { sessions, loadingSessionId, openSession };
}

function dedupeSidebarSessions(payload: unknown[]) {
	const byId = new Map<string, SidebarSession>();
	for (const item of payload) {
		if (!isSidebarSession(item)) continue;
		const previous = byId.get(item.session_id);
		if (!previous || item.modified_at >= previous.modified_at) {
			byId.set(item.session_id, item);
		}
	}
	return [...byId.values()].sort((a, b) => b.modified_at - a.modified_at);
}

function isSidebarSession(value: unknown): value is SidebarSession {
	if (!value || typeof value !== "object") return false;
	const item = value as Partial<SidebarSession>;
	return (
		typeof item.session_id === "string" &&
		typeof item.modified_at === "number" &&
		typeof item.preview === "string"
	);
}

function AccountMenu({
	account,
	onLogout,
	compact = false,
}: {
	account: ClawAccount;
	onLogout: () => void;
	compact?: boolean;
}) {
	const [open, setOpen] = useState(false);
	const [state, setState] = useState<ClawState | null>(null);
	const [sessions, setSessions] = useState<ClawSessionSummary[]>([]);
	const [activeSession, setActiveSession] = useState<ClawStoredSession | null>(
		null,
	);
	const [modelUsageOpen, setModelUsageOpen] = useState(false);

	useEffect(() => {
		if (!open) return;
		Promise.all([
			fetch("/api/claw/state", { cache: "no-store" }).then((res) =>
				res.ok ? res.json() : null,
			),
			fetch("/api/claw/sessions", { cache: "no-store" }).then((res) =>
				res.ok ? res.json() : [],
			),
		])
			.then(async ([statePayload, sessionsPayload]) => {
				const nextState = statePayload as ClawState | null;
				const nextSessions = Array.isArray(sessionsPayload)
					? (sessionsPayload as ClawSessionSummary[])
					: [];
				setState(nextState);
				setSessions(nextSessions);
				const activeSessionId = normalizeSessionId(
					window.localStorage.getItem("claw.activeSessionId") ??
						nextState?.active_session_id,
				);
				if (!activeSessionId) {
					setActiveSession(null);
					return;
				}
				window.localStorage.setItem("claw.activeSessionId", activeSessionId);
				const response = await fetch(`/api/claw/sessions/${activeSessionId}`, {
					cache: "no-store",
				});
				setActiveSession(response.ok ? await response.json() : null);
			})
			.catch(() => {
				setState(null);
				setSessions([]);
				setActiveSession(null);
			});
	}, [open]);

	async function logout() {
		await fetch("/api/claw/auth/logout", { method: "POST" });
		window.localStorage.removeItem("claw.activeSessionId");
		onLogout();
	}

	const totalToolCalls = sessions.reduce(
		(sum, session) => sum + (session.tool_calls ?? 0),
		0,
	);
	const modelUsage = aggregateUsageByModel(sessions);
	const modelUsageTotalTokens = modelUsage.reduce(
		(sum, item) => sum + item.totalTokens,
		0,
	);

	return (
		<PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
			<PopoverPrimitive.Trigger asChild>
				{compact ? (
					<button
						type="button"
						className="flex size-8 items-center justify-center rounded-full bg-sidebar-primary text-sidebar-primary-foreground transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
						aria-label="账号与统计"
						title={account.username}
					>
						<span className="font-medium text-xs">
							{account.username.slice(0, 2).toUpperCase()}
						</span>
					</button>
				) : (
					<button
						type="button"
						className="flex w-full items-center gap-2 rounded-lg px-2 py-2 text-left transition-colors hover:bg-sidebar-accent"
					>
						<div className="flex size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
							<UserIcon className="size-4" />
						</div>
						<div className="min-w-0 flex-1">
							<div className="truncate font-medium text-sm">
								{account.username}
							</div>
							<div className="text-muted-foreground text-xs">账号与统计</div>
						</div>
					</button>
				)}
			</PopoverPrimitive.Trigger>
			<PopoverPrimitive.Portal>
				<PopoverPrimitive.Content
					side="top"
					align="start"
					sideOffset={8}
					className="z-50 w-80 rounded-lg border bg-popover p-3 text-popover-foreground shadow-md outline-none"
				>
					<div className="mb-3 flex items-center gap-2">
						<div className="flex size-9 items-center justify-center rounded-lg bg-primary text-primary-foreground">
							<UserIcon className="size-4" />
						</div>
						<div className="min-w-0 flex-1">
							<div className="truncate font-medium">{account.username}</div>
							<div className="truncate text-muted-foreground text-xs">
								{state?.model ?? "模型配置读取中"}
							</div>
						</div>
						<Button
							variant="ghost"
							size="sm"
							className="h-8 shrink-0 gap-1.5 px-2 text-muted-foreground"
							onClick={logout}
						>
							<LogOutIcon className="size-3.5" />
							退出
						</Button>
					</div>
					<div className="grid grid-cols-3 gap-2">
						<AccountStat label="会话" value={sessions.length} />
						<AccountStat label="工具" value={totalToolCalls} />
						<AccountStat
							label="Token"
							value={activeSession?.usage?.total_tokens ?? "暂无"}
						/>
					</div>
					<div className="mt-3 rounded-md border bg-muted/30 p-2 text-xs">
						<div className="mb-1 flex items-center gap-1.5 font-medium">
							<BarChart3Icon className="size-3.5" />
							当前会话
						</div>
						<div className="grid grid-cols-2 gap-x-3 gap-y-1 text-muted-foreground">
							<span>输入 token</span>
							<span className="text-right">
								{activeSession?.usage?.input_tokens ?? "暂无"}
							</span>
							<span>输出 token</span>
							<span className="text-right">
								{activeSession?.usage?.output_tokens ?? "暂无"}
							</span>
							<span>工具调用</span>
							<span className="text-right">
								{activeSession?.tool_calls ?? "暂无"}
							</span>
						</div>
					</div>
					<div className="mt-3 rounded-md border bg-muted/30 p-2 text-xs">
						<button
							type="button"
							className="flex w-full items-center justify-between gap-2 text-left"
							onClick={() => setModelUsageOpen((value) => !value)}
						>
							<span className="font-medium">按模型统计</span>
							<span className="flex items-center gap-1 text-muted-foreground">
								{modelUsage.length
									? `${modelUsage.length} 个模型 · ${formatCompactNumber(modelUsageTotalTokens)} tokens`
									: "暂无"}
								<ChevronDownIcon
									className={`size-3.5 transition-transform ${
										modelUsageOpen ? "rotate-180" : ""
									}`}
								/>
							</span>
						</button>
						{modelUsageOpen && modelUsage.length ? (
							<div className="mt-2 flex max-h-48 flex-col gap-2 overflow-y-auto pr-1">
								{modelUsage.map((item) => (
									<div
										key={item.model}
										className="grid grid-cols-[1fr_auto] gap-2"
									>
										<span className="truncate text-muted-foreground">
											{item.model}
										</span>
										<span>{formatCompactNumber(item.totalTokens)} tokens</span>
										<span className="text-muted-foreground">
											{item.sessions} 会话 · {item.toolCalls} 工具
										</span>
										<span className="text-right text-muted-foreground">
											in {formatCompactNumber(item.inputTokens)} / out{" "}
											{formatCompactNumber(item.outputTokens)}
										</span>
									</div>
								))}
							</div>
						) : null}
						{modelUsageOpen && !modelUsage.length ? (
							<div className="text-muted-foreground">暂无可统计会话</div>
						) : null}
					</div>
					<div className="mt-3 flex items-center justify-between gap-2 border-t pt-3">
						<span className="text-muted-foreground text-xs">
							界面与模型配置
						</span>
						<div className="flex items-center gap-1">
							<ClawThemeSwitcher />
							<ClawLlmSettings />
						</div>
					</div>
				</PopoverPrimitive.Content>
			</PopoverPrimitive.Portal>
		</PopoverPrimitive.Root>
	);
}

function normalizeSessionId(value: unknown) {
	if (typeof value !== "string") return null;
	const trimmed = value.trim();
	return trimmed || null;
}

function aggregateUsageByModel(sessions: ClawSessionSummary[]) {
	const byModel = new Map<
		string,
		{
			model: string;
			sessions: number;
			toolCalls: number;
			inputTokens: number;
			outputTokens: number;
			totalTokens: number;
		}
	>();
	for (const session of sessions) {
		const model = session.model || "unknown";
		const current = byModel.get(model) ?? {
			model,
			sessions: 0,
			toolCalls: 0,
			inputTokens: 0,
			outputTokens: 0,
			totalTokens: 0,
		};
		current.sessions += 1;
		current.toolCalls += session.tool_calls ?? 0;
		current.inputTokens += session.usage?.input_tokens ?? 0;
		current.outputTokens += session.usage?.output_tokens ?? 0;
		current.totalTokens +=
			session.usage?.total_tokens ??
			(session.usage?.input_tokens ?? 0) + (session.usage?.output_tokens ?? 0);
		byModel.set(model, current);
	}
	return [...byModel.values()].sort((a, b) => b.totalTokens - a.totalTokens);
}

function formatCompactNumber(value: number) {
	return new Intl.NumberFormat("en", {
		notation: "compact",
		maximumFractionDigits: 1,
	}).format(value);
}

function AccountStat({
	label,
	value,
}: {
	label: string;
	value: string | number;
}) {
	return (
		<div className="rounded-md border bg-muted/30 px-2 py-1.5">
			<div className="font-medium text-sm">{value}</div>
			<div className="text-muted-foreground text-xs">{label}</div>
		</div>
	);
}
