"use client";

import { useAtomValue } from "jotai";
import {
	Bot,
	Check,
	ChevronDown,
	ChevronLeft,
	ChevronRight,
	ChevronUp,
	Pencil,
	ImageIcon,
	Layers,
	Plus,
	ScanEye,
	Search,
	Zap,
} from "lucide-react";
import type React from "react";
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import {
	globalImageGenConfigsAtom,
	imageGenConfigsAtom,
} from "@/atoms/image-gen-config/image-gen-config-query.atoms";
import { updateLLMPreferencesMutationAtom } from "@/atoms/new-llm-config/new-llm-config-mutation.atoms";
import {
	globalNewLLMConfigsAtom,
	llmPreferencesAtom,
	newLLMConfigsAtom,
} from "@/atoms/new-llm-config/new-llm-config-query.atoms";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import {
	globalVisionLLMConfigsAtom,
	visionLLMConfigsAtom,
} from "@/atoms/vision-llm-config/vision-llm-config-query.atoms";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
	Drawer,
	DrawerContent,
	DrawerHandle,
	DrawerHeader,
	DrawerTitle,
	DrawerTrigger,
} from "@/components/ui/drawer";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import type {
	GlobalImageGenConfig,
	GlobalNewLLMConfig,
	GlobalVisionLLMConfig,
	ImageGenerationConfig,
	NewLLMConfigPublic,
	VisionLLMConfig,
} from "@/contracts/types/new-llm-config.types";
import { useIsMobile } from "@/hooks/use-mobile";
import { getProviderIcon } from "@/lib/provider-icons";
import { cn } from "@/lib/utils";

// ─── Helpers ────────────────────────────────────────────────────────

const PROVIDER_NAMES: Record<string, string> = {
	OPENAI: "OpenAI",
	ANTHROPIC: "Anthropic",
	GOOGLE: "Google",
	AZURE: "Azure",
	AZURE_OPENAI: "Azure OpenAI",
	AWS_BEDROCK: "AWS Bedrock",
	BEDROCK: "Bedrock",
	DEEPSEEK: "DeepSeek",
	MISTRAL: "Mistral",
	COHERE: "Cohere",
	GITHUB_MODELS: "GitHub Models",
	GROQ: "Groq",
	OLLAMA: "Ollama",
	TOGETHER_AI: "Together AI",
	FIREWORKS_AI: "Fireworks AI",
	REPLICATE: "Replicate",
	HUGGINGFACE: "HuggingFace",
	PERPLEXITY: "Perplexity",
	XAI: "xAI",
	OPENROUTER: "OpenRouter",
	CEREBRAS: "Cerebras",
	SAMBANOVA: "SambaNova",
	VERTEX_AI: "Vertex AI",
	MINIMAX: "MiniMax",
	MOONSHOT: "Moonshot",
	ZHIPU: "Zhipu",
	DEEPINFRA: "DeepInfra",
	CLOUDFLARE: "Cloudflare",
	DATABRICKS: "Databricks",
	NSCALE: "NScale",
	RECRAFT: "Recraft",
	XINFERENCE: "XInference",
	CUSTOM: "Custom",
	AI21: "AI21",
	ALIBABA_QWEN: "Qwen",
	ANYSCALE: "Anyscale",
	COMETAPI: "CometAPI",
};

// Provider keys valid per model type, matching backend enums
// (LiteLLMProvider, ImageGenProvider, VisionProvider in db.py)
const LLM_PROVIDER_KEYS: string[] = [
	"OPENAI",
	"ANTHROPIC",
	"GOOGLE",
	"AZURE_OPENAI",
	"BEDROCK",
	"VERTEX_AI",
	"GROQ",
	"DEEPSEEK",
	"XAI",
	"MISTRAL",
	"COHERE",
	"OPENROUTER",
	"TOGETHER_AI",
	"FIREWORKS_AI",
	"REPLICATE",
	"PERPLEXITY",
	"OLLAMA",
	"CEREBRAS",
	"SAMBANOVA",
	"DEEPINFRA",
	"AI21",
	"ALIBABA_QWEN",
	"MOONSHOT",
	"ZHIPU",
	"MINIMAX",
	"HUGGINGFACE",
	"CLOUDFLARE",
	"DATABRICKS",
	"ANYSCALE",
	"COMETAPI",
	"GITHUB_MODELS",
	"CUSTOM",
];

const IMAGE_PROVIDER_KEYS: string[] = [
	"OPENAI",
	"AZURE_OPENAI",
	"GOOGLE",
	"VERTEX_AI",
	"BEDROCK",
	"RECRAFT",
	"OPENROUTER",
	"XINFERENCE",
	"NSCALE",
];

const VISION_PROVIDER_KEYS: string[] = [
	"OPENAI",
	"ANTHROPIC",
	"GOOGLE",
	"AZURE_OPENAI",
	"VERTEX_AI",
	"BEDROCK",
	"XAI",
	"OPENROUTER",
	"OLLAMA",
	"GROQ",
	"TOGETHER_AI",
	"FIREWORKS_AI",
	"DEEPSEEK",
	"MISTRAL",
	"CUSTOM",
];

const PROVIDER_KEYS_BY_TAB: Record<string, string[]> = {
	llm: LLM_PROVIDER_KEYS,
	image: IMAGE_PROVIDER_KEYS,
	vision: VISION_PROVIDER_KEYS,
};

function formatProviderName(provider: string): string {
	const key = provider.toUpperCase();
	return (
		PROVIDER_NAMES[key] ??
		provider.charAt(0).toUpperCase() + provider.slice(1).toLowerCase().replace(/_/g, " ")
	);
}

function normalizeText(input: string): string {
	return input
		.normalize("NFD")
		.replace(/\p{Diacritic}/gu, "")
		.toLowerCase()
		.replace(/[^a-z0-9]+/g, " ")
		.trim();
}

interface ConfigBase {
	id: number;
	name: string;
	model_name: string;
	provider: string;
}

function filterAndScore<T extends ConfigBase>(
	configs: T[],
	selectedProvider: string,
	searchQuery: string
): T[] {
	let result = configs;

	if (selectedProvider !== "all") {
		result = result.filter((c) => c.provider.toUpperCase() === selectedProvider);
	}

	if (!searchQuery.trim()) return result;

	const normalized = normalizeText(searchQuery);
	const tokens = normalized.split(/\s+/).filter(Boolean);

	const scored = result.map((c) => {
		const aggregate = normalizeText([c.name, c.model_name, c.provider].join(" "));
		let score = 0;
		if (aggregate.includes(normalized)) score += 5;
		for (const token of tokens) {
			if (aggregate.includes(token)) score += 1;
		}
		return { config: c, score };
	});

	return scored
		.filter((s) => s.score > 0)
		.sort((a, b) => b.score - a.score)
		.map((s) => s.config);
}

interface DisplayItem {
	config: ConfigBase & Record<string, unknown>;
	isGlobal: boolean;
	isAutoMode: boolean;
}

// ─── Component ──────────────────────────────────────────────────────

interface ModelSelectorProps {
	onEditLLM: (config: NewLLMConfigPublic | GlobalNewLLMConfig, isGlobal: boolean) => void;
	onAddNewLLM: (provider?: string) => void;
	onEditImage?: (config: ImageGenerationConfig | GlobalImageGenConfig, isGlobal: boolean) => void;
	onAddNewImage?: (provider?: string) => void;
	onEditVision?: (config: VisionLLMConfig | GlobalVisionLLMConfig, isGlobal: boolean) => void;
	onAddNewVision?: (provider?: string) => void;
	className?: string;
}

export function ModelSelector({
	onEditLLM,
	onAddNewLLM,
	onEditImage,
	onAddNewImage,
	onEditVision,
	onAddNewVision,
	className,
}: ModelSelectorProps) {
	const [open, setOpen] = useState(false);
	const [activeTab, setActiveTab] = useState<"llm" | "image" | "vision">("llm");
	const [searchQuery, setSearchQuery] = useState("");
	const [selectedProvider, setSelectedProvider] = useState<string>("all");
	const [focusedIndex, setFocusedIndex] = useState(-1);
	const [modelScrollPos, setModelScrollPos] = useState<"top" | "middle" | "bottom">("top");
	const [sidebarScrollPos, setSidebarScrollPos] = useState<"top" | "middle" | "bottom">("top");
	const providerSidebarRef = useRef<HTMLDivElement>(null);
	const modelListRef = useRef<HTMLDivElement>(null);
	const searchInputRef = useRef<HTMLInputElement>(null);
	const isMobile = useIsMobile();

	const handleOpenChange = useCallback(
		(next: boolean) => {
			if (next) {
				setSearchQuery("");
				setSelectedProvider("all");
				if (!isMobile) {
					requestAnimationFrame(() => searchInputRef.current?.focus());
				}
			}
			setOpen(next);
		},
		[isMobile]
	);

	const handleTabChange = useCallback(
		(next: "llm" | "image" | "vision") => {
			setActiveTab(next);
			setSelectedProvider("all");
			setSearchQuery("");
			setFocusedIndex(-1);
			setModelScrollPos("top");
			if (open && !isMobile) {
				requestAnimationFrame(() => searchInputRef.current?.focus());
			}
		},
		[open, isMobile]
	);

	const handleModelListScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
		const el = e.currentTarget;
		const atTop = el.scrollTop <= 2;
		const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= 2;
		setModelScrollPos(atTop ? "top" : atBottom ? "bottom" : "middle");
	}, []);

	const handleSidebarScroll = useCallback(
		(e: React.UIEvent<HTMLDivElement>) => {
			const el = e.currentTarget;
			if (isMobile) {
				const atStart = el.scrollLeft <= 2;
				const atEnd = el.scrollWidth - el.scrollLeft - el.clientWidth <= 2;
				setSidebarScrollPos(atStart ? "top" : atEnd ? "bottom" : "middle");
			} else {
				const atTop = el.scrollTop <= 2;
				const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= 2;
				setSidebarScrollPos(atTop ? "top" : atBottom ? "bottom" : "middle");
			}
		},
		[isMobile]
	);

	const scrollProviderSidebar = useCallback(
		(direction: "backward" | "forward") => {
			const el = providerSidebarRef.current;
			if (!el) return;
			const delta = isMobile
				? Math.max(56, Math.floor(el.clientWidth * 0.5))
				: Math.max(44, Math.floor(el.clientHeight * 0.4));

			if (isMobile) {
				el.scrollBy({
					left: direction === "backward" ? -delta : delta,
					behavior: "smooth",
				});
				return;
			}

			el.scrollBy({
				top: direction === "backward" ? -delta : delta,
				behavior: "smooth",
			});
		},
		[isMobile]
	);

	// Cmd/Ctrl+M shortcut (desktop only)
	useEffect(() => {
		if (isMobile) return;
		const handler = (e: KeyboardEvent) => {
			if ((e.metaKey || e.ctrlKey) && e.key === "m") {
				e.preventDefault();
				// setOpen((prev) => !prev);
				handleOpenChange(!open);
			}
		};
		document.addEventListener("keydown", handler);
		return () => document.removeEventListener("keydown", handler);
	}, [isMobile, open, handleOpenChange]);

	// ─── Data ───
	const { data: llmUserConfigs, isLoading: llmUserLoading } = useAtomValue(newLLMConfigsAtom);
	const { data: llmGlobalConfigs, isLoading: llmGlobalLoading } =
		useAtomValue(globalNewLLMConfigsAtom);
	const { data: preferences, isLoading: prefsLoading } = useAtomValue(llmPreferencesAtom);
	const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
	const { mutateAsync: updatePreferences } = useAtomValue(updateLLMPreferencesMutationAtom);
	const { data: imageGlobalConfigs, isLoading: imageGlobalLoading } =
		useAtomValue(globalImageGenConfigsAtom);
	const { data: imageUserConfigs, isLoading: imageUserLoading } = useAtomValue(imageGenConfigsAtom);
	const { data: visionGlobalConfigs, isLoading: visionGlobalLoading } = useAtomValue(
		globalVisionLLMConfigsAtom
	);
	const { data: visionUserConfigs, isLoading: visionUserLoading } =
		useAtomValue(visionLLMConfigsAtom);

	const isLoading =
		llmUserLoading ||
		llmGlobalLoading ||
		prefsLoading ||
		imageGlobalLoading ||
		imageUserLoading ||
		visionGlobalLoading ||
		visionUserLoading;

	// ─── Current selected configs ───
	const currentLLMConfig = useMemo(() => {
		if (!preferences) return null;
		const id = preferences.agent_llm_id;
		if (id === null || id === undefined) return null;
		if (id <= 0) return llmGlobalConfigs?.find((c) => c.id === id) ?? null;
		return llmUserConfigs?.find((c) => c.id === id) ?? null;
	}, [preferences, llmGlobalConfigs, llmUserConfigs]);

	const isLLMAutoMode =
		currentLLMConfig && "is_auto_mode" in currentLLMConfig && currentLLMConfig.is_auto_mode;

	const currentImageConfig = useMemo(() => {
		if (!preferences) return null;
		const id = preferences.image_generation_config_id;
		if (id === null || id === undefined) return null;
		return (
			imageGlobalConfigs?.find((c) => c.id === id) ??
			imageUserConfigs?.find((c) => c.id === id) ??
			null
		);
	}, [preferences, imageGlobalConfigs, imageUserConfigs]);

	const isImageAutoMode =
		currentImageConfig && "is_auto_mode" in currentImageConfig && currentImageConfig.is_auto_mode;

	const currentVisionConfig = useMemo(() => {
		if (!preferences) return null;
		const id = preferences.vision_llm_config_id;
		if (id === null || id === undefined) return null;
		return (
			visionGlobalConfigs?.find((c) => c.id === id) ??
			visionUserConfigs?.find((c) => c.id === id) ??
			null
		);
	}, [preferences, visionGlobalConfigs, visionUserConfigs]);

	const isVisionAutoMode =
		currentVisionConfig &&
		"is_auto_mode" in currentVisionConfig &&
		currentVisionConfig.is_auto_mode;

	// ─── Filtered configs (separate global / user for section headers) ───
	const filteredLLMGlobal = useMemo(
		() => filterAndScore(llmGlobalConfigs ?? [], selectedProvider, searchQuery),
		[llmGlobalConfigs, selectedProvider, searchQuery]
	);
	const filteredLLMUser = useMemo(
		() => filterAndScore(llmUserConfigs ?? [], selectedProvider, searchQuery),
		[llmUserConfigs, selectedProvider, searchQuery]
	);
	const filteredImageGlobal = useMemo(
		() => filterAndScore(imageGlobalConfigs ?? [], selectedProvider, searchQuery),
		[imageGlobalConfigs, selectedProvider, searchQuery]
	);
	const filteredImageUser = useMemo(
		() => filterAndScore(imageUserConfigs ?? [], selectedProvider, searchQuery),
		[imageUserConfigs, selectedProvider, searchQuery]
	);
	const filteredVisionGlobal = useMemo(
		() => filterAndScore(visionGlobalConfigs ?? [], selectedProvider, searchQuery),
		[visionGlobalConfigs, selectedProvider, searchQuery]
	);
	const filteredVisionUser = useMemo(
		() => filterAndScore(visionUserConfigs ?? [], selectedProvider, searchQuery),
		[visionUserConfigs, selectedProvider, searchQuery]
	);

	// Combined display list for keyboard navigation
	const currentDisplayItems: DisplayItem[] = useMemo(() => {
		const toItems = (configs: ConfigBase[], isGlobal: boolean): DisplayItem[] =>
			configs.map((c) => ({
				config: c as ConfigBase & Record<string, unknown>,
				isGlobal,
				isAutoMode:
					isGlobal && "is_auto_mode" in c && !!(c as Record<string, unknown>).is_auto_mode,
			}));

		const sortGlobalItems = (items: DisplayItem[]): DisplayItem[] =>
			[...items].sort((a, b) => {
				if (a.isAutoMode !== b.isAutoMode) return a.isAutoMode ? -1 : 1;
				const aPremium = !!(a.config as Record<string, unknown>).is_premium;
				const bPremium = !!(b.config as Record<string, unknown>).is_premium;
				if (aPremium !== bPremium) return aPremium ? 1 : -1;
				return 0;
			});

		switch (activeTab) {
			case "llm":
				return [
					...sortGlobalItems(toItems(filteredLLMGlobal, true)),
					...toItems(filteredLLMUser, false),
				];
			case "image":
				return [
					...sortGlobalItems(toItems(filteredImageGlobal, true)),
					...toItems(filteredImageUser, false),
				];
			case "vision":
				return [
					...sortGlobalItems(toItems(filteredVisionGlobal, true)),
					...toItems(filteredVisionUser, false),
				];
		}
	}, [
		activeTab,
		filteredLLMGlobal,
		filteredLLMUser,
		filteredImageGlobal,
		filteredImageUser,
		filteredVisionGlobal,
		filteredVisionUser,
	]);

	// ─── Provider sidebar data ───
	// Collect which providers actually have configured models for the active tab
	const configuredProviderSet = useMemo(() => {
		const configs =
			activeTab === "llm"
				? [...(llmGlobalConfigs ?? []), ...(llmUserConfigs ?? [])]
				: activeTab === "image"
					? [...(imageGlobalConfigs ?? []), ...(imageUserConfigs ?? [])]
					: [...(visionGlobalConfigs ?? []), ...(visionUserConfigs ?? [])];
		const set = new Set<string>();
		for (const c of configs) {
			if (c.provider) set.add(c.provider.toUpperCase());
		}
		return set;
	}, [
		activeTab,
		llmGlobalConfigs,
		llmUserConfigs,
		imageGlobalConfigs,
		imageUserConfigs,
		visionGlobalConfigs,
		visionUserConfigs,
	]);

	// Show only providers valid for the active tab; configured ones first
	const activeProviders = useMemo(() => {
		const tabKeys = PROVIDER_KEYS_BY_TAB[activeTab] ?? LLM_PROVIDER_KEYS;
		const configured = tabKeys.filter((p) => configuredProviderSet.has(p));
		const unconfigured = tabKeys.filter((p) => !configuredProviderSet.has(p));
		return ["all", ...configured, ...unconfigured];
	}, [activeTab, configuredProviderSet]);

	const providerModelCounts = useMemo(() => {
		const allConfigs =
			activeTab === "llm"
				? [...(llmGlobalConfigs ?? []), ...(llmUserConfigs ?? [])]
				: activeTab === "image"
					? [...(imageGlobalConfigs ?? []), ...(imageUserConfigs ?? [])]
					: [...(visionGlobalConfigs ?? []), ...(visionUserConfigs ?? [])];
		const counts: Record<string, number> = { all: allConfigs.length };
		for (const c of allConfigs) {
			const p = c.provider.toUpperCase();
			counts[p] = (counts[p] || 0) + 1;
		}
		return counts;
	}, [
		activeTab,
		llmGlobalConfigs,
		llmUserConfigs,
		imageGlobalConfigs,
		imageUserConfigs,
		visionGlobalConfigs,
		visionUserConfigs,
	]);

	// ─── Selection handlers ───
	const handleSelectLLM = useCallback(
		async (config: NewLLMConfigPublic | GlobalNewLLMConfig) => {
			if (currentLLMConfig?.id === config.id) {
				setOpen(false);
				return;
			}
			if (!searchSpaceId) {
				toast.error("No search space selected");
				return;
			}
			try {
				await updatePreferences({
					search_space_id: Number(searchSpaceId),
					data: { agent_llm_id: config.id },
				});
				toast.success(`Switched to ${config.name}`);
				setOpen(false);
			} catch {
				toast.error("Failed to switch model");
			}
		},
		[currentLLMConfig, searchSpaceId, updatePreferences]
	);

	const handleSelectImage = useCallback(
		async (configId: number) => {
			if (currentImageConfig?.id === configId) {
				setOpen(false);
				return;
			}
			if (!searchSpaceId) {
				toast.error("No search space selected");
				return;
			}
			try {
				await updatePreferences({
					search_space_id: Number(searchSpaceId),
					data: { image_generation_config_id: configId },
				});
				toast.success("Image model updated");
				setOpen(false);
			} catch {
				toast.error("Failed to switch image model");
			}
		},
		[currentImageConfig, searchSpaceId, updatePreferences]
	);

	const handleSelectVision = useCallback(
		async (configId: number) => {
			if (currentVisionConfig?.id === configId) {
				setOpen(false);
				return;
			}
			if (!searchSpaceId) {
				toast.error("No search space selected");
				return;
			}
			try {
				await updatePreferences({
					search_space_id: Number(searchSpaceId),
					data: { vision_llm_config_id: configId },
				});
				toast.success("Vision model updated");
				setOpen(false);
			} catch {
				toast.error("Failed to switch vision model");
			}
		},
		[currentVisionConfig, searchSpaceId, updatePreferences]
	);

	const handleSelectItem = useCallback(
		(item: DisplayItem) => {
			switch (activeTab) {
				case "llm":
					handleSelectLLM(item.config as NewLLMConfigPublic | GlobalNewLLMConfig);
					break;
				case "image":
					handleSelectImage(item.config.id);
					break;
				case "vision":
					handleSelectVision(item.config.id);
					break;
			}
		},
		[activeTab, handleSelectLLM, handleSelectImage, handleSelectVision]
	);

	const handleEditItem = useCallback(
		(e: React.MouseEvent, item: DisplayItem) => {
			e.stopPropagation();
			setOpen(false);
			switch (activeTab) {
				case "llm":
					onEditLLM(item.config as NewLLMConfigPublic | GlobalNewLLMConfig, item.isGlobal);
					break;
				case "image":
					onEditImage?.(item.config as ImageGenerationConfig | GlobalImageGenConfig, item.isGlobal);
					break;
				case "vision":
					onEditVision?.(item.config as VisionLLMConfig | GlobalVisionLLMConfig, item.isGlobal);
					break;
			}
		},
		[activeTab, onEditLLM, onEditImage, onEditVision]
	);

	// ─── Keyboard navigation ───
	// biome-ignore lint/correctness/useExhaustiveDependencies: searchQuery and selectedProvider are intentional triggers to reset focus
	useEffect(() => {
		setFocusedIndex(-1);
	}, [searchQuery, selectedProvider]);

	useEffect(() => {
		if (focusedIndex < 0 || !modelListRef.current) return;
		const items = modelListRef.current.querySelectorAll("[data-model-index]");
		items[focusedIndex]?.scrollIntoView({
			block: "nearest",
			behavior: "smooth",
		});
	}, [focusedIndex]);

	const handleKeyDown = useCallback(
		(e: React.KeyboardEvent<HTMLInputElement>) => {
			const count = currentDisplayItems.length;

			// Arrow Left/Right cycle provider filters
			if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
				e.preventDefault();
				const providers = activeProviders;
				const idx = providers.indexOf(selectedProvider);
				let next: number;
				if (e.key === "ArrowLeft") {
					next = idx > 0 ? idx - 1 : providers.length - 1;
				} else {
					next = idx < providers.length - 1 ? idx + 1 : 0;
				}
				setSelectedProvider(providers[next]);
				if (providerSidebarRef.current) {
					const buttons = providerSidebarRef.current.querySelectorAll("button");
					buttons[next]?.scrollIntoView({
						block: "nearest",
						inline: "nearest",
						behavior: "smooth",
					});
				}
				return;
			}

			if (count === 0) return;

			switch (e.key) {
				case "ArrowDown":
					e.preventDefault();
					setFocusedIndex((prev) => (prev < count - 1 ? prev + 1 : 0));
					break;
				case "ArrowUp":
					e.preventDefault();
					setFocusedIndex((prev) => (prev > 0 ? prev - 1 : count - 1));
					break;
				case "Enter":
					e.preventDefault();
					if (focusedIndex >= 0 && focusedIndex < count) {
						handleSelectItem(currentDisplayItems[focusedIndex]);
					}
					break;
				case "Home":
					e.preventDefault();
					setFocusedIndex(0);
					break;
				case "End":
					e.preventDefault();
					setFocusedIndex(count - 1);
					break;
			}
		},
		[currentDisplayItems, focusedIndex, activeProviders, selectedProvider, handleSelectItem]
	);

	// ─── Render: Provider sidebar ───
	const renderProviderSidebar = () => {
		const configuredCount = configuredProviderSet.size;

		return (
			<div
				className={cn(
					"shrink-0 border-border/50 flex relative",
					isMobile
						? "flex-row items-center border-b border-border/40"
						: "flex-col w-10 border-r"
				)}
			>
				{!isMobile && (
					<div
						className={cn(
							"absolute top-0 left-0 right-0 z-10 h-5 flex items-center justify-center transition-all duration-200 ease-out",
							sidebarScrollPos === "top"
								? "opacity-0 -translate-y-1 pointer-events-none"
								: "opacity-100 translate-y-0 pointer-events-auto"
						)}
					>
						<button
							type="button"
							aria-label="Scroll providers up"
							onClick={() => scrollProviderSidebar("backward")}
							className="flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground/90 hover:text-foreground hover:bg-accent/60 transition-colors"
						>
							<ChevronUp className="size-3" />
						</button>
					</div>
				)}
				{isMobile && (
					<div
						className={cn(
							"absolute left-0 top-0 bottom-0 z-10 w-5 flex items-center justify-center transition-all duration-200 ease-out pointer-events-none",
							sidebarScrollPos === "top"
								? "opacity-0 -translate-x-1"
								: "opacity-100 translate-x-0"
						)}
					>
						<ChevronLeft className="size-3 text-muted-foreground" />
					</div>
				)}
				<div
					ref={providerSidebarRef}
					onScroll={handleSidebarScroll}
					className={cn(
						isMobile
							? "flex flex-row gap-0.5 px-1 py-1.5 overflow-x-auto [&::-webkit-scrollbar]:h-0 [&::-webkit-scrollbar-track]:bg-transparent"
							: "flex flex-col gap-0.5 p-1 overflow-y-auto flex-1 [&::-webkit-scrollbar]:w-0 [&::-webkit-scrollbar-track]:bg-transparent"
					)}
					style={
						isMobile
							? {
									maskImage: `linear-gradient(to right, ${sidebarScrollPos === "top" ? "black" : "transparent"}, black 24px, black calc(100% - 24px), ${sidebarScrollPos === "bottom" ? "black" : "transparent"})`,
									WebkitMaskImage: `linear-gradient(to right, ${sidebarScrollPos === "top" ? "black" : "transparent"}, black 24px, black calc(100% - 24px), ${sidebarScrollPos === "bottom" ? "black" : "transparent"})`,
								}
							: {
									maskImage: `linear-gradient(to bottom, ${sidebarScrollPos === "top" ? "black" : "transparent"}, black 32px, black calc(100% - 32px), ${sidebarScrollPos === "bottom" ? "black" : "transparent"})`,
									WebkitMaskImage: `linear-gradient(to bottom, ${sidebarScrollPos === "top" ? "black" : "transparent"}, black 32px, black calc(100% - 32px), ${sidebarScrollPos === "bottom" ? "black" : "transparent"})`,
								}
					}
				>
					{activeProviders.map((provider, idx) => {
						const isAll = provider === "all";
						const isActive = selectedProvider === provider;
						const count = providerModelCounts[provider] || 0;
						const isConfigured = isAll || configuredProviderSet.has(provider);

						// Separator between configured and unconfigured providers
						// idx 0 is "all", configured run from 1..configuredCount, unconfigured start at configuredCount+1
						const showSeparator = !isAll && idx === configuredCount + 1 && configuredCount > 0;

						return (
							<Fragment key={provider}>
								{showSeparator &&
									(isMobile ? (
										<div className="w-px h-5 bg-border/60 shrink-0 self-center mx-0.5" />
									) : (
										<div className="h-px w-5 bg-border/60 mx-auto my-0.5" />
									))}
								<Tooltip>
									<TooltipTrigger asChild>
										<button
											type="button"
											onClick={() => setSelectedProvider(provider)}
											tabIndex={-1}
											className={cn(
												"relative flex items-center justify-center rounded-md transition-all duration-150",
												isMobile ? "p-2 shrink-0" : "p-1.5 w-full",
												isActive
													? "bg-primary/10 text-primary"
													: isConfigured
														? "hover:bg-accent/60 text-muted-foreground hover:text-foreground"
														: "opacity-50 hover:opacity-80 hover:bg-accent/40 text-muted-foreground"
											)}
										>
											{isAll ? (
												<Layers className="size-4" />
											) : (
												getProviderIcon(provider, {
													className: "size-4",
												})
											)}
										</button>
									</TooltipTrigger>
									<TooltipContent side={isMobile ? "bottom" : "right"}>
										{isAll ? "All Models" : formatProviderName(provider)}
										{isConfigured ? ` (${count})` : " (not configured)"}
									</TooltipContent>
								</Tooltip>
							</Fragment>
						);
					})}
				</div>
				{!isMobile && (
					<div
						className={cn(
							"absolute bottom-0 left-0 right-0 z-10 h-5 flex items-center justify-center transition-all duration-200 ease-out",
							sidebarScrollPos === "bottom"
								? "opacity-0 translate-y-1 pointer-events-none"
								: "opacity-100 translate-y-0 pointer-events-auto"
						)}
					>
						<button
							type="button"
							aria-label="Scroll providers down"
							onClick={() => scrollProviderSidebar("forward")}
							className="flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground/90 hover:text-foreground hover:bg-accent/60 transition-colors"
						>
							<ChevronDown className="size-3" />
						</button>
					</div>
				)}
				{isMobile && (
					<div
						className={cn(
							"absolute right-0 top-0 bottom-0 z-10 w-5 flex items-center justify-center transition-all duration-200 ease-out pointer-events-none",
							sidebarScrollPos === "bottom"
								? "opacity-0 translate-x-1"
								: "opacity-100 translate-x-0"
						)}
					>
						<ChevronRight className="size-3 text-muted-foreground" />
					</div>
				)}
			</div>
		);
	};

	// ─── Render: Model card ───
	const getSelectedId = () => {
		switch (activeTab) {
			case "llm":
				return currentLLMConfig?.id;
			case "image":
				return currentImageConfig?.id;
			case "vision":
				return currentVisionConfig?.id;
		}
	};

	const renderModelCard = (item: DisplayItem, index: number) => {
		const { config, isAutoMode } = item;
		const isSelected = getSelectedId() === config.id;
		const isFocused = focusedIndex === index;
		const hasCitations = "citations_enabled" in config && !!config.citations_enabled;

		return (
			<div
				key={`${activeTab}-${item.isGlobal ? "g" : "u"}-${config.id}`}
				data-model-index={index}
				role="option"
				tabIndex={isMobile ? -1 : 0}
				aria-selected={isSelected}
				onClick={() => handleSelectItem(item)}
				onKeyDown={
					isMobile
						? undefined
						: (e) => {
								if (e.key === "Enter" || e.key === " ") {
									e.preventDefault();
									handleSelectItem(item);
								}
							}
				}
				onMouseEnter={() => setFocusedIndex(index)}
				className={cn(
					"group flex items-center gap-2.5 px-3 py-2 rounded-xl cursor-pointer",
					"transition-all duration-150 mx-2",
					"hover:bg-accent/40",
					isSelected && "bg-primary/6 dark:bg-primary/8",
					isFocused && "bg-accent/50"
				)}
			>
				{/* Provider icon */}
				<div className="shrink-0">
					{getProviderIcon(config.provider as string, {
						isAutoMode,
						className: "size-5",
					})}
				</div>

				{/* Model info */}
				<div className="flex-1 min-w-0">
					<div className="flex items-center gap-1.5">
						<span className="font-medium text-sm truncate">{config.name}</span>
						{isAutoMode && (
							<Badge
								variant="secondary"
								className="text-[9px] px-1 py-0 h-3.5 bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300 border-0"
							>
								Recommended
							</Badge>
						)}
						{"is_premium" in config && (config as Record<string, unknown>).is_premium ? (
							<Badge
								variant="secondary"
								className="text-[9px] px-1 py-0 h-3.5 bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-300 border-0"
							>
								Premium
							</Badge>
						) : "is_premium" in config &&
							!(config as Record<string, unknown>).is_premium &&
							!isAutoMode ? (
							<Badge
								variant="secondary"
								className="text-[9px] px-1 py-0 h-3.5 bg-emerald-100 text-emerald-700 dark:bg-emerald-900/50 dark:text-emerald-300 border-0"
							>
								Free
							</Badge>
						) : null}
					</div>
					<div className="flex items-center gap-1.5 mt-0.5">
						<span className="text-xs text-muted-foreground truncate">
							{isAutoMode ? "Auto Mode" : (config.model_name as string)}
						</span>
						{!isAutoMode && hasCitations && (
							<Badge
								variant="secondary"
								className="text-[10px] px-1.5 py-0.5 border-0 text-muted-foreground bg-muted"
							>
								Citations
							</Badge>
						)}
					</div>
				</div>

				{/* Actions */}
				<div className="flex items-center gap-1 shrink-0">
					{!isAutoMode && (
						<Button
							variant="ghost"
							size="icon"
							className="size-7 rounded-md hover:bg-muted opacity-0 group-hover:opacity-100 transition-opacity"
							onClick={(e) => handleEditItem(e, item)}
						>
							<Pencil className="size-3.5 text-muted-foreground" />
						</Button>
					)}
					{isSelected && <Check className="size-4 text-primary shrink-0" />}
				</div>
			</div>
		);
	};

	// ─── Render: Full content ───
	const renderContent = () => {
		const globalItems = currentDisplayItems.filter((i) => i.isGlobal);
		const userItems = currentDisplayItems.filter((i) => !i.isGlobal);
		const globalStartIdx = 0;
		const userStartIdx = globalItems.length;

		const addHandler =
			activeTab === "llm" ? onAddNewLLM : activeTab === "image" ? onAddNewImage : onAddNewVision;
		const addLabel =
			activeTab === "llm"
				? "Add Model"
				: activeTab === "image"
					? "Add Image Model"
					: "Add Vision Model";

		return (
			<div className="flex flex-col w-full overflow-hidden">
				{/* Tab header */}
				<div className="border-b border-border/80 dark:border-neutral-800">
					<div className="w-full grid grid-cols-3 h-11">
						{(
							[
								{
									value: "llm" as const,
									icon: Zap,
									label: "LLM",
								},
								{
									value: "image" as const,
									icon: ImageIcon,
									label: "Image",
								},
								{
									value: "vision" as const,
									icon: ScanEye,
									label: "Vision",
								},
							] as const
						).map(({ value, icon: Icon, label }) => (
							<button
								key={value}
								type="button"
								// onClick={() => setActiveTab(value)}
								onClick={() => handleTabChange(value)}
								className={cn(
									"flex items-center justify-center gap-1.5 text-sm font-medium transition-all duration-200 border-b-[1.5px]",
									activeTab === value
										? "border-foreground dark:border-white text-foreground"
										: "border-transparent text-muted-foreground hover:text-foreground/70"
								)}
							>
								<Icon className="size-3.5" />
								{label}
							</button>
						))}
					</div>
				</div>

				{/* Two-pane layout */}
				<div className={cn("flex", isMobile ? "flex-col h-[60vh]" : "flex-row h-[380px]")}>
					{/* Provider sidebar */}
					{renderProviderSidebar()}

					{/* Main content */}
					<div className="flex flex-col min-w-0 min-h-0 flex-1 overflow-hidden">
						{/* Search */}
						<div className="relative">
							<Search className="absolute left-3 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground/100 pointer-events-none" />
							<input
								ref={searchInputRef}
								placeholder="Search models"
								value={searchQuery}
								onChange={(e) => setSearchQuery(e.target.value)}
								onKeyDown={isMobile ? undefined : handleKeyDown}
								role="combobox"
								aria-expanded={true}
								aria-controls="model-selector-list"
								className={cn(
									"w-full pl-8 pr-3 py-2.5 text-sm bg-transparent",
									"focus:outline-none",
									"placeholder:text-muted-foreground"
								)}
							/>
						</div>

						{/* Provider header when filtered */}
						{selectedProvider !== "all" && (
							<div className="flex items-center gap-2 px-3 py-1.5">
								{getProviderIcon(selectedProvider, {
									className: "size-4",
								})}
								<span className="text-sm font-medium">{formatProviderName(selectedProvider)}</span>
								<span className="text-xs text-muted-foreground ml-auto">
									{configuredProviderSet.has(selectedProvider)
										? `${providerModelCounts[selectedProvider] || 0} models`
										: "Not configured"}
								</span>
							</div>
						)}

						{/* Model list */}
						<div
							id="model-selector-list"
							ref={modelListRef}
							role="listbox"
							className="overflow-y-auto flex-1 py-1 space-y-1 flex flex-col"
							onScroll={handleModelListScroll}
							style={{
								maskImage: `linear-gradient(to bottom, ${modelScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${modelScrollPos === "bottom" ? "black" : "transparent"})`,
								WebkitMaskImage: `linear-gradient(to bottom, ${modelScrollPos === "top" ? "black" : "transparent"}, black 16px, black calc(100% - 16px), ${modelScrollPos === "bottom" ? "black" : "transparent"})`,
							}}
						>
							{currentDisplayItems.length === 0 ? (
								<div className="flex-1 flex flex-col items-center justify-center gap-3 px-4">
									{selectedProvider !== "all" && !configuredProviderSet.has(selectedProvider) ? (
										<>
											<div className="opacity-40">
												{getProviderIcon(selectedProvider, {
													className: "size-10",
												})}
											</div>
											<p className="text-sm font-medium text-muted-foreground">
												No {formatProviderName(selectedProvider)} models configured
											</p>
											<p className="text-xs text-muted-foreground/60 text-center">
												Add a model with this provider to get started
											</p>
											{addHandler && (
												<Button
													variant="secondary"
													size="sm"
													className="mt-1"
													onClick={() => {
														setOpen(false);
														addHandler(selectedProvider !== "all" ? selectedProvider : undefined);
													}}
												>
													{addLabel}
												</Button>
											)}
										</>
									) : searchQuery ? (
										<>
											<Search className="size-8 text-muted-foreground" />
											<p className="text-sm text-muted-foreground">No models found</p>
											<p className="text-xs text-muted-foreground/60">
												Try a different search term
											</p>
										</>
									) : (
										<>
											<p className="text-sm font-medium text-muted-foreground">
												No models configured
											</p>
											<p className="text-xs text-muted-foreground/60 text-center">
												Configure models in your search space settings
											</p>
										</>
									)}
								</div>
							) : (
								<>
									{globalItems.length > 0 && (
										<>
											<div className="flex items-center gap-2 px-3 py-1.5 text-[12px] font-semibold text-muted-foreground tracking-wider">
												Global Models
											</div>
											{globalItems.map((item, i) => renderModelCard(item, globalStartIdx + i))}
										</>
									)}
									{globalItems.length > 0 && userItems.length > 0 && (
										<div className="my-1.5 mx-4 h-px bg-border/60" />
									)}
									{userItems.length > 0 && (
										<>
											<div className="flex items-center gap-2 px-3 py-1.5 text-[12px] font-semibold text-muted-foreground tracking-wider">
												Your Configurations
											</div>
											{userItems.map((item, i) => renderModelCard(item, userStartIdx + i))}
										</>
									)}
								</>
							)}
						</div>

						{/* Add model button */}
						{addHandler && (
							<div className="p-2">
								<Button
									variant="ghost"
									size="sm"
									className="w-full justify-start gap-2 h-9 rounded-lg hover:bg-accent/50 dark:hover:bg-white/[0.06]"
									onClick={() => {
										setOpen(false);
										addHandler(selectedProvider !== "all" ? selectedProvider : undefined);
									}}
								>
									<Plus className="size-4 text-primary" />
									<span className="text-sm font-medium">{addLabel}</span>
								</Button>
							</div>
						)}
					</div>
				</div>
			</div>
		);
	};

	// ─── Trigger button ───
	const triggerButton = (
		<Button
			variant="ghost"
			size="sm"
			role="combobox"
			aria-expanded={open}
			className={cn(
				"h-8 gap-2 px-3 text-sm bg-main-panel hover:bg-accent/50 dark:hover:bg-white/[0.06] border border-border/40 select-none",
				className
			)}
		>
			{isLoading ? (
				<>
					<Spinner size="sm" className="text-muted-foreground" />
					<span className="text-muted-foreground hidden md:inline">Loading</span>
				</>
			) : (
				<>
					{/* LLM */}
					{currentLLMConfig ? (
						<>
							{getProviderIcon(currentLLMConfig.provider, {
								isAutoMode: isLLMAutoMode ?? false,
							})}
							<span className="max-w-[100px] md:max-w-[120px] truncate hidden md:inline">
								{currentLLMConfig.name}
							</span>
						</>
					) : (
						<>
							<Bot className="size-4 text-muted-foreground" />
							<span className="text-muted-foreground hidden md:inline">Select Model</span>
						</>
					)}
					<div className="h-4 w-px bg-border/60 dark:bg-white/10 mx-0.5" />
					{/* Image */}
					{currentImageConfig ? (
						<>
							{getProviderIcon(currentImageConfig.provider, {
								isAutoMode: isImageAutoMode ?? false,
							})}
							<span className="max-w-[80px] md:max-w-[100px] truncate hidden md:inline">
								{currentImageConfig.name}
							</span>
						</>
					) : (
						<ImageIcon className="size-4 text-muted-foreground" />
					)}
					<div className="h-4 w-px bg-border/60 dark:bg-white/10 mx-0.5" />
					{/* Vision */}
					{currentVisionConfig ? (
						<>
							{getProviderIcon(currentVisionConfig.provider, {
								isAutoMode: isVisionAutoMode ?? false,
							})}
							<span className="max-w-[80px] md:max-w-[100px] truncate hidden md:inline">
								{currentVisionConfig.name}
							</span>
						</>
					) : (
						<ScanEye className="size-4 text-muted-foreground" />
					)}
				</>
			)}
			<ChevronDown className="h-3.5 w-3.5 text-muted-foreground ml-1 shrink-0" />
		</Button>
	);

	// ─── Shell: Drawer on mobile, Popover on desktop ───
	if (isMobile) {
		return (
			<Drawer open={open} onOpenChange={handleOpenChange}>
				<DrawerTrigger asChild>{triggerButton}</DrawerTrigger>
				<DrawerContent className="max-h-[85vh]">
					<DrawerHandle />
					<DrawerHeader className="pb-0">
						<DrawerTitle>Select Model</DrawerTitle>
					</DrawerHeader>
					<div className="flex-1 overflow-hidden">{renderContent()}</div>
				</DrawerContent>
			</Drawer>
		);
	}

	return (
		<Popover open={open} onOpenChange={handleOpenChange}>
			<PopoverTrigger asChild>{triggerButton}</PopoverTrigger>
			<PopoverContent
				className="w-[300px] md:w-[380px] p-0 rounded-lg shadow-lg overflow-hidden bg-white border-border/60 dark:bg-neutral-900 dark:border dark:border-white/5 select-none"
				align="start"
				sideOffset={8}
				onCloseAutoFocus={(e) => e.preventDefault()}
			>
				{renderContent()}
			</PopoverContent>
		</Popover>
	);
}
