"use client"

import { useState, useMemo, useEffect, useCallback, useRef } from "react"
import Image from "next/image"
import { dmSans125ClassName, dmSansClassName } from "@/lib/fonts"
import { Dialog, DialogContent, DialogTitle } from "@repo/ui/components/dialog"
import { Drawer, DrawerContent, DrawerTitle } from "@repo/ui/components/drawer"
import { Avatar, AvatarFallback, AvatarImage } from "@ui/components/avatar"
import { cn } from "@lib/utils"
import { useIsMobile } from "@hooks/use-mobile"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import {
	XIcon,
	Search,
	FolderIcon,
	LayoutGrid,
	Plus,
	Trash2,
	Clock,
	ArrowRight,
	BookOpen,
	Loader,
	Pencil,
	Check,
	Lock,
} from "lucide-react"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import { toast } from "sonner"
import { DEFAULT_PROJECT_ID, SHARED_TEAM_BRAIN_TAG } from "@lib/constants"
import { authClient } from "@lib/auth"
import { useAuth } from "@lib/auth-context"
import type { ContainerTagListType } from "@lib/types"
import {
	compareSpacesUserFirst,
	isOwnConversationSpace,
	spaceSelectorDisplayName,
} from "@/lib/ingest-auto-space"
import {
	detectPluginSpace,
	pluginInitial,
	type PluginSpaceInfo,
} from "@/lib/plugin-space"
import { usePluginSpaceMeta } from "@/hooks/use-plugin-space-meta"
import { groupAgentSpaces, type AgentSpaceGroup } from "@/lib/agent-space"
import {
	PLUGIN_CATALOG,
	spacePluginIdToCatalogId,
	type PluginInfo,
} from "@/lib/plugin-catalog"
import { INSET, InstallSteps, PillButton } from "./integrations/install-steps"
import { useProjectMutations } from "@/hooks/use-project-mutations"
import { AUTO_CHAT_SPACE_ID } from "@/lib/chat-auto-space"
import NovaOrb from "@/components/nova/nova-orb"
import { AutoSpaceIcon } from "@/components/nova/auto-space-icon"
import { SpaceGlyph } from "./space-glyph"
import { useHasCompanyBrain } from "@/hooks/use-company-brain"
import { Logo } from "@ui/assets/Logo"

interface SelectSpacesModalProps {
	isOpen: boolean
	onClose: () => void
	selectedProjects: string[]
	onApply: (selected: string[]) => void
	projects: ContainerTagListType[]
	recents?: string[]
	showNewSpace?: boolean
	includeAuto?: boolean
	onNewSpace?: () => void
	enableDelete?: boolean
	onDeleteRequest?: (project: {
		id: string
		name: string
		containerTag: string
	}) => void
	onBulkDeleteRequest?: (
		projects: {
			id: string
			name: string
			containerTag: string
		}[],
	) => void
}

type CategoryId =
	| "all"
	| "my"
	| `plugin:${PluginSpaceInfo["pluginId"]}`
	| `discover:${string}`

type Category = {
	id: CategoryId
	label: string
	iconSrc: string | null
	emoji: string | null
	count: number
}

const AGENT_CATALOG_IDS = [
	"claude_code",
	"codex",
	"opencode",
	"cursor",
] as const

export function SelectSpacesModal({
	isOpen,
	onClose,
	selectedProjects,
	onApply,
	projects,
	recents,
	showNewSpace = false,
	includeAuto = false,
	onNewSpace,
	enableDelete = false,
	onDeleteRequest,
	onBulkDeleteRequest,
}: SelectSpacesModalProps) {
	const [searchQuery, setSearchQuery] = useState("")
	const [isBulkDeleteMode, setIsBulkDeleteMode] = useState(false)
	const [bulkDeleteTags, setBulkDeleteTags] = useState<Set<string>>(new Set())
	const [lastBulkDeleteTag, setLastBulkDeleteTag] = useState<string | null>(
		null,
	)
	const [editingProject, setEditingProject] = useState<{
		id: string
		containerTag: string
		originalName: string
		name: string
	} | null>(null)
	const editInputRef = useRef<HTMLInputElement | null>(null)
	const editingContainerTag = editingProject?.containerTag
	const currentSelection = selectedProjects[0] ?? ""
	const selectedTagSet = useMemo(
		() => new Set(selectedProjects),
		[selectedProjects],
	)
	const isMobile = useIsMobile()

	const pluginTags = useMemo(
		() =>
			projects
				.filter((p) => !!detectPluginSpace(p.containerTag))
				.map((p) => p.containerTag),
		[projects],
	)

	const pluginMetaMap = usePluginSpaceMeta(pluginTags)
	const hasCompanyBrain = useHasCompanyBrain()
	const agentGroups = useMemo(
		() => groupAgentSpaces(projects, pluginMetaMap),
		[projects, pluginMetaMap],
	)
	const agentGroupByTag = useMemo(() => {
		const map = new Map<string, AgentSpaceGroup<ContainerTagListType>>()
		for (const group of agentGroups) {
			for (const tag of group.containerTags) map.set(tag, group)
		}
		return map
	}, [agentGroups])

	const allSpaces = useMemo(() => {
		const rest = projects
			.filter((p) => p.containerTag !== DEFAULT_PROJECT_ID)
			.filter((p) => {
				const group = agentGroupByTag.get(p.containerTag)
				return !group || group.representative.containerTag === p.containerTag
			})
			.sort(compareSpacesUserFirst)
		// Company brain orgs use real Private + Team Brain spaces; skip the
		// synthetic "My Space" default that would otherwise duplicate Private.
		if (hasCompanyBrain) return rest
		const defaultSpace = {
			id: "default",
			name: "My Space",
			emoji: "📁",
			containerTag: DEFAULT_PROJECT_ID,
			isExperimental: false,
			isNova: false,
			createdAt: "",
			updatedAt: "",
		} as ContainerTagListType
		return [defaultSpace, ...rest]
	}, [projects, hasCompanyBrain, agentGroupByTag])

	const { categories, connectedCatalogIds } = useMemo<{
		categories: Category[]
		connectedCatalogIds: Set<string>
	}>(() => {
		const pluginCounts = new Map<
			PluginSpaceInfo["pluginId"],
			{ label: string; iconSrc: string | null; count: number }
		>()
		let myCount = 0
		for (const p of allSpaces) {
			const plugin = detectPluginSpace(p.containerTag)
			if (plugin) {
				const prev = pluginCounts.get(plugin.pluginId)
				pluginCounts.set(plugin.pluginId, {
					label: plugin.label,
					iconSrc: plugin.iconSrc,
					count: (prev?.count ?? 0) + 1,
				})
			} else {
				myCount += 1
			}
		}
		const pluginCats: Category[] = Array.from(pluginCounts.entries())
			.map(([id, info]) => ({
				id: `plugin:${id}` as CategoryId,
				label: info.label,
				iconSrc: info.iconSrc,
				emoji: null,
				count: info.count,
			}))
			.sort((a, b) => b.count - a.count || a.label.localeCompare(b.label))
		const connectedIds = new Set<string>()
		for (const pluginId of pluginCounts.keys()) {
			if (pluginId === "agents") {
				for (const catalogId of AGENT_CATALOG_IDS) connectedIds.add(catalogId)
				continue
			}
			const catalogId = spacePluginIdToCatalogId(pluginId)
			if (catalogId) connectedIds.add(catalogId)
		}
		return {
			categories: [
				{
					id: "all",
					label: "All Spaces",
					iconSrc: null,
					emoji: null,
					count: allSpaces.length,
				},
				{
					id: "my",
					label: "My Spaces",
					iconSrc: null,
					emoji: "📁",
					count: myCount,
				},
				...pluginCats,
			],
			connectedCatalogIds: connectedIds,
		}
	}, [allSpaces])

	const defaultCategory = useMemo<CategoryId>(() => {
		if (!currentSelection) return "all"
		if (currentSelection === AUTO_CHAT_SPACE_ID) return "all"
		const plugin = detectPluginSpace(currentSelection)
		if (plugin) return `plugin:${plugin.pluginId}`
		return "my"
	}, [currentSelection])

	const [activeCategory, setActiveCategory] =
		useState<CategoryId>(defaultCategory)
	const activeDiscoverId = activeCategory.startsWith("discover:")
		? activeCategory.slice("discover:".length)
		: null

	useEffect(() => {
		if (isOpen) setActiveCategory(defaultCategory)
	}, [isOpen, defaultCategory])

	useEffect(() => {
		if (!activeDiscoverId) return
		setIsBulkDeleteMode(false)
		setBulkDeleteTags(new Set())
		setLastBulkDeleteTag(null)
	}, [activeDiscoverId])

	const { org, user } = useAuth()
	const queryClient = useQueryClient()
	const [connectingPluginId, setConnectingPluginId] = useState<string | null>(
		null,
	)
	const [newKey, setNewKey] = useState<{
		pluginId: string
		key: string
	} | null>(null)
	const { updateProjectMutation } = useProjectMutations()

	const { data: availablePluginsData } = useQuery({
		queryKey: ["plugins"],
		queryFn: async () => {
			const API_URL =
				process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai"
			const res = await fetch(`${API_URL}/v3/auth/plugins`, {
				credentials: "include",
			})
			if (!res.ok) throw new Error("Failed to fetch plugins")
			return (await res.json()) as { plugins: string[] }
		},
		staleTime: 5 * 60 * 1000,
		enabled: isOpen && !!activeDiscoverId,
	})

	const { data: apiKeys = [] } = useQuery({
		queryKey: ["api-keys", org?.id],
		enabled: isOpen && !!activeDiscoverId && !!org?.id,
		queryFn: async () => {
			if (!org?.id) return []
			const data = (await authClient.apiKey.list({
				fetchOptions: { query: { metadata: { organizationId: org.id } } },
			})) as unknown as { metadata?: Record<string, unknown> | null }[]
			return data.filter((key) => key.metadata?.organizationId === org.id)
		},
	})

	const apiKeyConnectedIds = useMemo(() => {
		const ids = new Set<string>()
		for (const key of apiKeys) {
			if (!key.metadata) continue
			try {
				const metadata =
					typeof key.metadata === "string"
						? (JSON.parse(key.metadata) as {
								sm_type?: string
								sm_client?: string
							})
						: (key.metadata as { sm_type?: string; sm_client?: string })
				if (metadata.sm_type === "plugin_auth" && metadata.sm_client) {
					ids.add(metadata.sm_client)
				}
			} catch {}
		}
		return ids
	}, [apiKeys])
	const availablePluginIds = useMemo(
		() => availablePluginsData?.plugins ?? Object.keys(PLUGIN_CATALOG),
		[availablePluginsData],
	)
	const agentDiscoverCatalogIds = useMemo(
		() =>
			AGENT_CATALOG_IDS.filter(
				(id) =>
					availablePluginIds.includes(id) &&
					(!apiKeyConnectedIds.has(id) && !connectedCatalogIds.has(id)
						? true
						: newKey?.pluginId === id),
			),
		[
			availablePluginIds,
			apiKeyConnectedIds,
			connectedCatalogIds,
			newKey?.pluginId,
		],
	)

	const discoverCategories = useMemo<Category[]>(() => {
		const categories: Category[] = availablePluginIds
			.filter((id) => !!PLUGIN_CATALOG[id])
			.filter(
				(id) =>
					!AGENT_CATALOG_IDS.some((agentCatalogId) => agentCatalogId === id),
			)
			.filter(
				(id) => !apiKeyConnectedIds.has(id) && !connectedCatalogIds.has(id),
			)
			.map((id) => {
				const info = PLUGIN_CATALOG[id] as PluginInfo
				return {
					id: `discover:${id}` as CategoryId,
					label: info.name,
					iconSrc: info.icon,
					emoji: null,
					count: 0,
				}
			})
		if (agentDiscoverCatalogIds.length > 0) {
			categories.unshift({
				id: "discover:agents",
				label: "Agents",
				iconSrc: null,
				emoji: null,
				count: 0,
			})
		}
		return categories
	}, [
		availablePluginIds,
		agentDiscoverCatalogIds.length,
		apiKeyConnectedIds,
		connectedCatalogIds,
	])

	const connectMutation = useMutation({
		mutationFn: async (pluginId: string) => {
			const API_URL =
				process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai"
			const params = new URLSearchParams({ client: pluginId })
			const res = await fetch(`${API_URL}/v3/auth/key?${params}`, {
				credentials: "include",
			})
			if (!res.ok) {
				const errorData = (await res.json().catch(() => ({}))) as {
					message?: string
				}
				throw new Error(errorData.message || "Failed to create plugin key")
			}
			return (await res.json()) as { key: string }
		},
		onMutate: (pluginId) => setConnectingPluginId(pluginId),
		onError: (err) => {
			toast.error("Failed to connect plugin", {
				description: err instanceof Error ? err.message : "Unknown error",
			})
		},
		onSettled: () => {
			setConnectingPluginId(null)
			queryClient.invalidateQueries({ queryKey: ["api-keys", org?.id] })
		},
		onSuccess: (data, pluginId) => {
			setNewKey({ pluginId, key: data.key })
			toast.success("Plugin connected!")
		},
	})

	useEffect(() => {
		if (!isOpen) {
			setNewKey(null)
			setEditingProject(null)
			setIsBulkDeleteMode(false)
			setBulkDeleteTags(new Set())
			setLastBulkDeleteTag(null)
		}
	}, [isOpen])

	useEffect(() => {
		if (!editingContainerTag) return
		const frame = requestAnimationFrame(() => {
			editInputRef.current?.focus()
			editInputRef.current?.select()
		})
		return () => cancelAnimationFrame(frame)
	}, [editingContainerTag])

	const handleOpenChange = useCallback(
		(open: boolean) => {
			if (!open) {
				onClose()
				setSearchQuery("")
				setEditingProject(null)
				setIsBulkDeleteMode(false)
				setBulkDeleteTags(new Set())
				setLastBulkDeleteTag(null)
			}
		},
		[onClose],
	)

	const handleSelect = useCallback(
		(containerTag: string) => {
			setEditingProject(null)
			setIsBulkDeleteMode(false)
			setBulkDeleteTags(new Set())
			setLastBulkDeleteTag(null)
			onApply(
				agentGroupByTag.get(containerTag)?.containerTags ?? [containerTag],
			)
			setSearchQuery("")
		},
		[agentGroupByTag, onApply],
	)

	const handleSelectAuto = useCallback(() => {
		setEditingProject(null)
		setIsBulkDeleteMode(false)
		setBulkDeleteTags(new Set())
		setLastBulkDeleteTag(null)
		onApply([AUTO_CHAT_SPACE_ID])
		setSearchQuery("")
	}, [onApply])

	const handleBulkModeToggle = useCallback(() => {
		setEditingProject(null)
		setBulkDeleteTags(new Set())
		setLastBulkDeleteTag(null)
		setIsBulkDeleteMode((prev) => !prev)
	}, [])

	const startEditing = useCallback((project: ContainerTagListType) => {
		const name = project.name ?? project.containerTag
		setEditingProject({
			id: project.id,
			containerTag: project.containerTag,
			originalName: name,
			name,
		})
	}, [])

	const cancelEditing = useCallback(() => {
		setEditingProject(null)
	}, [])

	const saveEditing = useCallback(() => {
		if (!editingProject) return
		const nextName = editingProject.name.trim()
		const currentName = editingProject.originalName.trim()
		if (!nextName || nextName === currentName) return

		updateProjectMutation.mutate(
			{ containerTag: editingProject.containerTag, name: nextName },
			{
				onSuccess: () => setEditingProject(null),
			},
		)
	}, [editingProject, updateProjectMutation])

	const handleEditKeyDown = useCallback(
		(e: React.KeyboardEvent<HTMLInputElement>) => {
			if (e.key === "Enter") {
				e.preventDefault()
				saveEditing()
			}
			if (e.key === "Escape") {
				e.preventDefault()
				cancelEditing()
			}
		},
		[cancelEditing, saveEditing],
	)

	const filteredProjects = useMemo(() => {
		const byCategory = allSpaces.filter((p) => {
			if (activeCategory === "all") return true
			const plugin = detectPluginSpace(p.containerTag)
			if (activeCategory === "my") return !plugin
			return plugin && `plugin:${plugin.pluginId}` === activeCategory
		})
		if (!searchQuery.trim()) return byCategory
		const query = searchQuery.trim().toLowerCase()
		return byCategory.filter((p) => {
			const plugin = detectPluginSpace(p.containerTag)
			const agentGroup = agentGroupByTag.get(p.containerTag)
			const projectName =
				agentGroup?.projectName ??
				agentGroup?.label ??
				pluginMetaMap.get(p.containerTag)?.projectName
			const displayName = spaceSelectorDisplayName(p, p.containerTag, {
				currentUserId: user?.id,
			})
			return (
				p.containerTag.toLowerCase().includes(query) ||
				(p.name ?? "").toLowerCase().includes(query) ||
				displayName.toLowerCase().includes(query) ||
				(plugin?.label.toLowerCase().includes(query) ?? false) ||
				(plugin?.projectId?.toLowerCase().includes(query) ?? false) ||
				(projectName?.toLowerCase().includes(query) ?? false)
			)
		})
	}, [
		allSpaces,
		activeCategory,
		searchQuery,
		pluginMetaMap,
		agentGroupByTag,
		user?.id,
	])

	const recentProjects = useMemo<ContainerTagListType[]>(() => {
		if (!recents?.length) return []
		if (searchQuery.trim()) return []
		if (activeCategory !== "all") return []
		const byTag = new Map(allSpaces.map((p) => [p.containerTag, p]))
		const out: ContainerTagListType[] = []
		const seen = new Set<string>()
		for (const tag of recents) {
			const representativeTag =
				agentGroupByTag.get(tag)?.representative.containerTag ?? tag
			const p = byTag.get(representativeTag)
			if (p && !seen.has(p.containerTag)) {
				seen.add(p.containerTag)
				out.push(p)
			}
			if (out.length >= 5) break
		}
		return out
	}, [recents, searchQuery, activeCategory, allSpaces, agentGroupByTag])

	const recentSet = useMemo(
		() => new Set(recentProjects.map((p) => p.containerTag)),
		[recentProjects],
	)

	const mainList = useMemo(
		() =>
			recentSet.size > 0
				? filteredProjects.filter((p) => !recentSet.has(p.containerTag))
				: filteredProjects,
		[filteredProjects, recentSet],
	)

	const showAutoRow = useMemo(() => {
		if (!includeAuto) return false
		if (isBulkDeleteMode) return false
		if (activeCategory !== "all" && activeCategory !== "my") return false
		const query = searchQuery.trim().toLowerCase()
		if (!query) return true
		return (
			"auto".includes(query) ||
			"let nova choose the right spaces".includes(query) ||
			"discover spaces".includes(query)
		)
	}, [includeAuto, isBulkDeleteMode, activeCategory, searchQuery])

	const visibleBulkDeleteTags = useMemo(
		() =>
			[...recentProjects, ...mainList]
				.filter(
					(project) =>
						project.containerTag !== DEFAULT_PROJECT_ID &&
						!agentGroupByTag.has(project.containerTag),
				)
				.map((project) => project.containerTag),
		[recentProjects, mainList, agentGroupByTag],
	)

	const toggleBulkDeleteTag = useCallback(
		(containerTag: string, shiftKey = false) => {
			setBulkDeleteTags((prev) => {
				const next = new Set(prev)
				const currentIndex = visibleBulkDeleteTags.indexOf(containerTag)
				const anchorIndex = lastBulkDeleteTag
					? visibleBulkDeleteTags.indexOf(lastBulkDeleteTag)
					: -1

				if (shiftKey && currentIndex !== -1 && anchorIndex !== -1) {
					const start = Math.min(anchorIndex, currentIndex)
					const end = Math.max(anchorIndex, currentIndex)
					for (const tag of visibleBulkDeleteTags.slice(start, end + 1)) {
						next.add(tag)
					}
				} else if (next.has(containerTag)) {
					next.delete(containerTag)
				} else {
					next.add(containerTag)
				}

				return next
			})
			setLastBulkDeleteTag(containerTag)
		},
		[lastBulkDeleteTag, visibleBulkDeleteTags],
	)

	const bulkDeleteProjects = useMemo(
		() =>
			allSpaces
				.filter(
					(project) =>
						project.containerTag !== DEFAULT_PROJECT_ID &&
						!agentGroupByTag.has(project.containerTag) &&
						bulkDeleteTags.has(project.containerTag),
				)
				.map((project) => ({
					id: project.id,
					name: spaceSelectorDisplayName(project, project.containerTag, {
						currentUserId: user?.id,
					}),
					containerTag: project.containerTag,
				})),
		[allSpaces, bulkDeleteTags, agentGroupByTag, user?.id],
	)

	const bulkDeleteCount = bulkDeleteProjects.length

	const renderRow = useCallback(
		(project: ContainerTagListType) => {
			const agentGroup = agentGroupByTag.get(project.containerTag)
			const isSelected = agentGroup
				? agentGroup.containerTags.some((tag) => selectedTagSet.has(tag))
				: currentSelection === project.containerTag
			const plugin = detectPluginSpace(project.containerTag)
			const pluginProjectName =
				agentGroup?.projectName ??
				agentGroup?.label ??
				pluginMetaMap.get(project.containerTag)?.projectName
			const pluginIdLabel = pluginProjectName || plugin?.projectId
			const displayName = spaceSelectorDisplayName(
				project,
				project.containerTag,
				{
					currentUserId: user?.id,
				},
			)
			const isDefault = project.containerTag === DEFAULT_PROJECT_ID
			const isOwnSpace = isOwnConversationSpace(project, user?.id)
			const isCbSpace =
				hasCompanyBrain && !plugin && !isOwnSpace && !!project.visibility
			const isShared = project.visibility === "public"
			const orgName = org?.name ?? "your team"
			const orgMembers = org?.members ?? []
			const memberCount = orgMembers.length
			const isDefaultBrain = project.containerTag === SHARED_TEAM_BRAIN_TAG
			const descriptor = isCbSpace
				? isShared
					? `${orgName} · ${memberCount} ${
							memberCount === 1 ? "member" : "members"
						}`
					: "Only you"
				: null
			const canEdit = !isDefault && !plugin && !isOwnSpace
			const canBulkDelete = enableDelete && !isDefault && !agentGroup
			const isEditing = editingProject?.containerTag === project.containerTag
			const isBulkDeleteSelected = bulkDeleteTags.has(project.containerTag)
			const trimmedEditName = editingProject?.name.trim() ?? ""
			const isSaveDisabled =
				!trimmedEditName ||
				trimmedEditName === editingProject?.originalName.trim() ||
				updateProjectMutation.isPending
			const handleRowAction = (
				e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
			) => {
				if (isEditing) return
				if (isBulkDeleteMode) {
					if (canBulkDelete) {
						toggleBulkDeleteTag(project.containerTag, e.shiftKey)
					}
					return
				}
				handleSelect(project.containerTag)
			}
			return (
				<div
					key={project.containerTag}
					className={cn(
						"group flex min-w-0 max-w-full items-center gap-3 w-full px-3 py-2.5 rounded-[12px] transition-colors",
						(isBulkDeleteMode ? isBulkDeleteSelected : isSelected)
							? "bg-[#14161A] shadow-inside-out"
							: "hover:bg-[#14161A]/50",
						isBulkDeleteMode &&
							!canBulkDelete &&
							"cursor-not-allowed opacity-45",
					)}
				>
					<button
						type="button"
						onClick={handleRowAction}
						disabled={isBulkDeleteMode && !canBulkDelete}
						aria-label={
							isBulkDeleteMode ? "Select space for deletion" : "Select space"
						}
						aria-pressed={isBulkDeleteMode ? isBulkDeleteSelected : isSelected}
						className={cn(
							"w-4 h-4 rounded-full border-2 flex items-center justify-center shrink-0 transition-colors cursor-pointer disabled:cursor-not-allowed",
							isBulkDeleteMode
								? isBulkDeleteSelected
									? "border-red-400 bg-red-400/10"
									: "border-[#737373]"
								: isSelected
									? "border-[#4BA0FA]"
									: "border-[#737373]",
						)}
					>
						{isBulkDeleteMode ? (
							isBulkDeleteSelected && <Check className="size-3 text-red-300" />
						) : isSelected ? (
							<div className="w-2 h-2 rounded-full bg-[#4BA0FA]" />
						) : null}
					</button>
					{isEditing ? (
						<div className="flex min-w-0 flex-1 items-center gap-2">
							<SpaceGlyph
								emoji={project.emoji}
								size={18}
								className="shrink-0"
							/>
							<input
								type="text"
								value={editingProject.name}
								ref={editInputRef}
								onChange={(e) =>
									setEditingProject((prev) =>
										prev ? { ...prev, name: e.target.value } : prev,
									)
								}
								onKeyDown={handleEditKeyDown}
								className={cn(
									"min-w-0 flex-1 rounded-[9px] border border-[rgba(82,89,102,0.35)] bg-[#0D121A] px-2.5 py-1.5 text-sm font-medium text-[#fafafa] shadow-inside-out placeholder:text-[#737373] focus:outline-none focus:ring-1 focus:ring-[rgba(75,160,250,0.45)]",
									dmSansClassName(),
								)}
								aria-label="Space name"
							/>
							<button
								type="button"
								onClick={saveEditing}
								disabled={isSaveDisabled}
								aria-label="Save space name"
								className="shrink-0 rounded-full p-1.5 text-[#4BA0FA] transition-colors hover:bg-[#4BA0FA]/15 disabled:cursor-not-allowed disabled:opacity-35"
							>
								<Check className="size-3.5" />
							</button>
							<button
								type="button"
								onClick={cancelEditing}
								disabled={updateProjectMutation.isPending}
								aria-label="Cancel editing space name"
								className="shrink-0 rounded-full p-1.5 text-[#737373] transition-colors hover:bg-[#737373]/15 hover:text-[#fafafa] disabled:cursor-not-allowed disabled:opacity-35"
							>
								<XIcon className="size-3.5" />
							</button>
						</div>
					) : (
						<button
							type="button"
							onClick={handleRowAction}
							disabled={isBulkDeleteMode && !canBulkDelete}
							className="flex min-w-0 flex-1 items-center gap-3 text-left cursor-pointer focus:outline-none focus:ring-0 disabled:cursor-not-allowed"
						>
							{plugin ? (
								plugin.pluginId === "agents" ? null : plugin.iconSrc ? (
									<Image
										src={plugin.iconSrc}
										alt=""
										width={20}
										height={20}
										className="shrink-0 rounded-[4px]"
										aria-hidden
									/>
								) : (
									<span
										className="shrink-0 flex items-center justify-center w-5 h-5 rounded-[4px] bg-[#1E232B] text-[#FAFAFA] text-[11px] font-semibold uppercase"
										aria-hidden
									>
										{pluginInitial(plugin.label)}
									</span>
								)
							) : isOwnSpace ? (
								<NovaOrb size={20} className="shrink-0 blur-[0.55px]!" />
							) : isCbSpace ? (
								isShared ? (
									<span className="shrink-0 flex items-center" aria-hidden>
										{orgMembers.slice(0, 3).map((m, i) => (
											<Avatar
												key={m.id}
												className={cn(
													"size-6 ring-2 ring-[#14161A]",
													i > 0 && "-ml-2",
												)}
											>
												<AvatarImage
													src={m.user?.image ?? ""}
													alt=""
													className="object-cover"
												/>
												<AvatarFallback className="bg-[#1E232B] text-white text-[10px] font-medium">
													{(m.user?.name ?? m.user?.email ?? "U")
														.charAt(0)
														.toUpperCase()}
												</AvatarFallback>
											</Avatar>
										))}
										{memberCount > 3 && (
											<span className="-ml-2 flex size-6 items-center justify-center rounded-full bg-[#1E232B] text-[#A3A3A3] text-[9px] font-medium ring-2 ring-[#14161A]">
												+{memberCount - 3}
											</span>
										)}
									</span>
								) : (
									<span className="shrink-0 relative" aria-hidden>
										<Avatar className="size-6">
											<AvatarImage
												src={user?.image ?? ""}
												alt=""
												className="object-cover"
											/>
											<AvatarFallback className="bg-[#1E232B] text-white text-[10px] font-medium">
												{(user?.name ?? user?.email ?? "U")
													.charAt(0)
													.toUpperCase()}
											</AvatarFallback>
										</Avatar>
										<span className="absolute -right-1 -bottom-1 flex size-3.5 items-center justify-center rounded-full bg-[#14161A] text-[#A3A3A3]">
											<Lock className="size-2" />
										</span>
									</span>
								)
							) : (
								<SpaceGlyph
									emoji={project.emoji}
									size={20}
									className="shrink-0"
								/>
							)}
							<span className="flex min-w-0 flex-1 flex-col">
								<span
									className="truncate text-[#fafafa] text-sm font-medium"
									title={plugin ? project.containerTag : displayName}
								>
									{plugin ? (
										plugin.pluginId === "agents" ? (
											(pluginIdLabel ?? plugin.label)
										) : (
											<>
												{plugin.label}
												{pluginIdLabel && (
													<span className="ml-1.5 text-[12px] text-[#737373]">
														· {pluginIdLabel}
													</span>
												)}
											</>
										)
									) : (
										displayName
									)}
								</span>
								{descriptor && (
									<span className="truncate text-[11px] text-[#737373]">
										{descriptor}
									</span>
								)}
							</span>
							{isCbSpace && isDefaultBrain && (
								<span className="ml-2 shrink-0 rounded-[4px] bg-[#4BA0FA]/15 px-1.5 py-0.5 text-[10px] font-medium text-[#4BA0FA]">
									Default
								</span>
							)}
						</button>
					)}
					{canEdit && !isEditing && !isBulkDeleteMode && (
						<button
							type="button"
							onClick={(e) => {
								e.stopPropagation()
								startEditing(project)
							}}
							aria-label="Rename space"
							className="shrink-0 opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 transition-opacity p-1.5 rounded-full text-[#737373] hover:bg-[#737373]/15 hover:text-[#fafafa] cursor-pointer focus:outline-none"
						>
							<Pencil className="size-3.5" />
						</button>
					)}
					{enableDelete &&
						!isDefault &&
						!agentGroup &&
						!isEditing &&
						!isBulkDeleteMode &&
						onDeleteRequest && (
							<button
								type="button"
								onClick={(e) => {
									e.stopPropagation()
									onDeleteRequest({
										id: project.id,
										name: displayName,
										containerTag: project.containerTag,
									})
								}}
								aria-label="Delete space"
								className="shrink-0 opacity-0 group-hover:opacity-100 transition-opacity p-1.5 rounded-full hover:bg-red-500/15 cursor-pointer focus:outline-none"
							>
								<Trash2 className="size-3.5 text-red-400" />
							</button>
						)}
				</div>
			)
		},
		[
			cancelEditing,
			agentGroupByTag,
			bulkDeleteTags,
			currentSelection,
			selectedTagSet,
			editingProject,
			enableDelete,
			handleEditKeyDown,
			handleSelect,
			hasCompanyBrain,
			isBulkDeleteMode,
			onDeleteRequest,
			pluginMetaMap,
			saveEditing,
			startEditing,
			toggleBulkDeleteTag,
			updateProjectMutation.isPending,
			user?.id,
			org?.name,
			org?.members,
			user?.email,
			user?.image,
			user?.name,
		],
	)

	const renderAutoRow = useCallback(() => {
		const isSelected = currentSelection === AUTO_CHAT_SPACE_ID
		return (
			<div
				key={AUTO_CHAT_SPACE_ID}
				className={cn(
					"group flex min-w-0 max-w-full items-center gap-3 w-full px-3 py-2.5 rounded-[12px] transition-colors",
					isSelected
						? "bg-[#14161A] shadow-inside-out"
						: "hover:bg-[#14161A]/50",
				)}
			>
				<div
					className={cn(
						"w-4 h-4 rounded-full border-2 flex items-center justify-center shrink-0 transition-colors",
						isSelected ? "border-[#4BA0FA]" : "border-[#737373]",
					)}
				>
					{isSelected && <div className="w-2 h-2 rounded-full bg-[#4BA0FA]" />}
				</div>
				<button
					type="button"
					onClick={handleSelectAuto}
					className="flex min-w-0 flex-1 items-center gap-3 text-left cursor-pointer focus:outline-none focus:ring-0"
				>
					<AutoSpaceIcon size={20} />
					<span className="min-w-0 flex-1 truncate text-[#fafafa] text-sm font-medium">
						Auto
						<span className="ml-1.5 text-[12px] text-[#737373]">
							· Nova chooses spaces
						</span>
					</span>
				</button>
			</div>
		)
	}, [currentSelection, handleSelectAuto])

	const renderCategoryChip = (category: Category, isDiscover: boolean) => {
		const isActive = activeCategory === category.id
		return (
			<button
				key={category.id}
				type="button"
				onClick={() => setActiveCategory(category.id)}
				className={cn(
					"flex shrink-0 items-center gap-2 whitespace-nowrap rounded-full border px-3 py-2 transition-colors",
					isActive
						? "border-[#2261CA33] bg-[#00173C] text-[#fafafa]"
						: "border-[#161F2C] bg-[#0D121A] text-[#A1A1AA]",
					isDiscover && !isActive && "opacity-60",
				)}
			>
				<span className="flex size-[18px] shrink-0 items-center justify-center">
					{category.id === "all" ? (
						<LayoutGrid
							className={cn(
								"size-4",
								isActive ? "text-[#fafafa]" : "text-[#737373]",
							)}
						/>
					) : category.id === "plugin:agents" ||
						category.id === "discover:agents" ? (
						<Logo className="h-[18px] w-[22px]" />
					) : category.iconSrc ? (
						<Image
							src={category.iconSrc}
							alt=""
							width={18}
							height={18}
							className="rounded-[3px]"
							aria-hidden
						/>
					) : category.emoji ? (
						<SpaceGlyph emoji={category.emoji} size={16} />
					) : category.id.startsWith("plugin:") ? (
						<span
							className="flex h-[18px] w-[18px] items-center justify-center rounded-[3px] bg-[#1E232B] text-[10px] font-semibold uppercase text-[#FAFAFA]"
							aria-hidden
						>
							{pluginInitial(category.label)}
						</span>
					) : (
						<FolderIcon
							className={cn(
								"size-4",
								isActive ? "text-[#fafafa]" : "text-[#737373]",
							)}
						/>
					)}
				</span>
				<span className="text-[13px] font-medium">{category.label}</span>
				{isDiscover ? (
					<ArrowRight className="size-3.5 text-[#737373]" />
				) : (
					<span className="text-[11px] tabular-nums text-[#737373]">
						{category.count}
					</span>
				)}
			</button>
		)
	}

	const discoverPanelContent =
		activeDiscoverId === "agents" ? (
			<AgentsDiscoverPanel
				catalogIds={agentDiscoverCatalogIds}
				connectingPluginId={connectingPluginId}
				newKey={newKey}
				onConnect={(catalogId) => connectMutation.mutate(catalogId)}
				onDismissKey={() => setNewKey(null)}
			/>
		) : (
			<DiscoverPanel
				catalogId={activeDiscoverId ?? ""}
				isConnecting={connectingPluginId === activeDiscoverId}
				newKey={newKey?.pluginId === activeDiscoverId ? newKey.key : null}
				onConnect={() => {
					if (activeDiscoverId) connectMutation.mutate(activeDiscoverId)
				}}
				onDismissKey={() => setNewKey(null)}
			/>
		)

	const rightPanelContent = activeCategory.startsWith("discover:") ? (
		discoverPanelContent
	) : (
		<>
			<div className="relative shrink-0">
				<Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-[#737373]" />
				<input
					type="text"
					value={searchQuery}
					onChange={(e) => setSearchQuery(e.target.value)}
					placeholder="Search spaces..."
					className={cn(
						"w-full rounded-[12px] bg-[#14161A] py-2.5 pl-10 pr-4 text-[14px] text-[#fafafa] shadow-inside-out placeholder:text-[#737373] focus:outline-none",
						dmSansClassName(),
					)}
				/>
			</div>
			<div className="min-h-0 flex-1 overflow-x-hidden overflow-y-auto scrollbar-thin pr-1">
				{filteredProjects.length === 0 ? (
					<p className="py-8 text-center text-sm text-[#737373]">
						No spaces found
					</p>
				) : (
					<div className="flex flex-col gap-1">
						{showAutoRow && (
							<>
								<div className="px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
									Mode
								</div>
								{renderAutoRow()}
								<div className="my-1.5 h-px bg-[rgba(82,89,102,0.18)]" />
							</>
						)}
						{recentProjects.length > 0 && (
							<>
								<div className="flex items-center gap-1.5 px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
									<Clock className="size-3" />
									Recently used
								</div>
								{recentProjects.map(renderRow)}
								<div className="my-1.5 h-px bg-[rgba(82,89,102,0.18)]" />
								<div className="px-3 pt-0.5 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
									All spaces
								</div>
							</>
						)}
						{hasCompanyBrain && recentProjects.length === 0
							? (() => {
									const shared = mainList.filter(
										(p) => p.visibility === "public",
									)
									const personal = mainList.filter(
										(p) => p.visibility !== "public",
									)
									return (
										<>
											{shared.length > 0 && (
												<>
													<div className="px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
														Shared
													</div>
													{shared.map(renderRow)}
												</>
											)}
											{personal.length > 0 && (
												<>
													<div className="px-3 pt-2 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
														Personal
													</div>
													{personal.map(renderRow)}
												</>
											)}
										</>
									)
								})()
							: mainList.map(renderRow)}
					</div>
				)}
			</div>
		</>
	)

	const footerContent = !activeCategory.startsWith("discover:") &&
		(isBulkDeleteMode || (showNewSpace && onNewSpace)) && (
			<div className="flex shrink-0 items-center justify-between gap-3 border-t border-[rgba(82,89,102,0.18)] px-4 py-3">
				{isBulkDeleteMode ? (
					<>
						<p className="min-w-0 text-[13px] font-medium text-[#737373]">
							{bulkDeleteCount === 0
								? "No spaces selected"
								: `${bulkDeleteCount} ${
										bulkDeleteCount === 1 ? "space" : "spaces"
									} selected`}
						</p>
						<div className="flex shrink-0 items-center gap-2">
							<button
								type="button"
								onClick={handleBulkModeToggle}
								className={cn(
									"px-3 py-2 text-[13px] font-medium text-[#737373] transition-colors hover:text-[#fafafa]",
									dmSansClassName(),
								)}
							>
								Cancel
							</button>
							<button
								type="button"
								disabled={bulkDeleteCount === 0}
								onClick={() => {
									if (bulkDeleteCount === 0) return
									onBulkDeleteRequest?.(bulkDeleteProjects)
									setIsBulkDeleteMode(false)
									setBulkDeleteTags(new Set())
									setLastBulkDeleteTag(null)
								}}
								className={cn(
									"flex items-center gap-2 rounded-full bg-red-600 px-4 py-2 text-[13px] font-medium text-white transition-colors hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-40",
									dmSansClassName(),
								)}
							>
								<Trash2 className="size-4" />
								Delete selected
							</button>
						</div>
					</>
				) : (
					<>
						<span />
						{showNewSpace && onNewSpace && (
							<button
								type="button"
								onClick={onNewSpace}
								className={cn(
									"flex items-center gap-2 rounded-full bg-[#14161A] px-4 py-2 text-[13px] font-medium text-[#fafafa] shadow-inside-out transition-colors hover:bg-[#121820] focus:outline-none focus:ring-0",
									dmSansClassName(),
								)}
							>
								<Plus className="size-4" />
								New space
							</button>
						)}
					</>
				)}
			</div>
		)

	if (isMobile) {
		return (
			<Drawer open={isOpen} onOpenChange={handleOpenChange}>
				<DrawerContent
					className={cn(
						"flex h-[85dvh] flex-col gap-0 overflow-hidden border-none bg-[#1B1F24] p-0",
						dmSansClassName(),
					)}
				>
					<DrawerTitle className="sr-only">Select Space</DrawerTitle>
					<div className="flex shrink-0 items-start justify-between gap-3 px-4 pt-1">
						<div className="space-y-1">
							<p
								className={cn(
									"font-semibold text-[#fafafa]",
									dmSans125ClassName(),
								)}
							>
								Select Space
							</p>
							<p className="text-[13px] font-medium leading-[1.35] text-[#737373]">
								{isBulkDeleteMode
									? "Choose spaces to permanently delete"
									: "Filter your memories by space"}
							</p>
						</div>
						<div className="flex shrink-0 items-center gap-2">
							{enableDelete && onBulkDeleteRequest && !activeDiscoverId && (
								<button
									type="button"
									onClick={handleBulkModeToggle}
									className={cn(
										"flex h-8 items-center gap-1.5 rounded-full bg-[#0D121A] px-2.5 text-[12px] font-medium transition-colors",
										isBulkDeleteMode ? "text-[#fafafa]" : "text-[#737373]",
									)}
									style={{
										boxShadow:
											"inset 1.313px 1.313px 3.938px 0px rgba(0,0,0,0.7)",
									}}
								>
									<Trash2 className="size-3.5" />
									{isBulkDeleteMode ? "Cancel" : "Bulk delete"}
								</button>
							)}
							<button
								type="button"
								onClick={() => handleOpenChange(false)}
								aria-label="Close"
								className="flex size-8 shrink-0 items-center justify-center rounded-full border border-[rgba(115,115,115,0.2)] bg-[#0D121A]"
								style={{
									boxShadow:
										"inset 1.313px 1.313px 3.938px 0px rgba(0,0,0,0.7)",
								}}
							>
								<XIcon stroke="#737373" className="size-4" />
							</button>
						</div>
					</div>

					<div className="mt-3 flex shrink-0 gap-1.5 overflow-x-auto px-4 pb-2 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
						{categories.map((category) => renderCategoryChip(category, false))}
						{discoverCategories.length > 0 && (
							<>
								<div className="mx-0.5 my-1 w-px shrink-0 bg-[rgba(82,89,102,0.25)]" />
								{discoverCategories.map((category) =>
									renderCategoryChip(category, true),
								)}
							</>
						)}
					</div>

					<div className="mt-3 flex min-h-0 flex-1 flex-col gap-3 overflow-hidden px-4 pb-2">
						{rightPanelContent}
					</div>

					{footerContent}
				</DrawerContent>
			</Drawer>
		)
	}

	return (
		<Dialog open={isOpen} onOpenChange={handleOpenChange}>
			<DialogContent
				className={cn(
					"w-[calc(100vw-1rem)]! max-w-[720px]! max-h-[calc(100dvh-1rem)] min-w-0 border-none bg-[#1B1F24] flex flex-col p-0 gap-0 rounded-[22px] overflow-hidden sm:w-[92vw]!",
					dmSansClassName(),
				)}
				style={{
					display: "flex",
					boxShadow:
						"0 2.842px 14.211px 0 rgba(0, 0, 0, 0.25), 0.711px 0.711px 0.711px 0 rgba(255, 255, 255, 0.10) inset",
				}}
				showCloseButton={false}
			>
				<div className="flex items-start justify-between gap-4 px-4 pt-4">
					<div className="pl-1 space-y-1 flex-1">
						<DialogTitle
							className={cn(
								"font-semibold text-[#fafafa]",
								dmSans125ClassName(),
							)}
						>
							Select Space
						</DialogTitle>
						<p className="text-[#737373] font-medium text-[14px] leading-[1.35]">
							{isBulkDeleteMode
								? "Choose spaces to permanently delete"
								: "Filter your memories by space"}
						</p>
					</div>
					<div className="flex shrink-0 items-center gap-2">
						{enableDelete && onBulkDeleteRequest && !activeDiscoverId && (
							<button
								type="button"
								onClick={handleBulkModeToggle}
								className={cn(
									"flex h-7 items-center gap-1.5 rounded-full bg-[#0D121A] px-2.5 text-[12px] font-medium transition-colors hover:bg-[#121820] focus:outline-none",
									isBulkDeleteMode ? "text-[#fafafa]" : "text-[#737373]",
								)}
								style={{
									boxShadow:
										"inset 1.313px 1.313px 3.938px 0px rgba(0,0,0,0.7)",
								}}
							>
								<Trash2 className="size-3.5" />
								{isBulkDeleteMode ? "Cancel" : "Bulk delete"}
							</button>
						)}
						<DialogPrimitive.Close
							className="bg-[#0D121A] w-7 h-7 flex items-center justify-center focus:ring-ring rounded-full transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 border border-[rgba(115,115,115,0.2)] shrink-0"
							style={{
								boxShadow: "inset 1.313px 1.313px 3.938px 0px rgba(0,0,0,0.7)",
							}}
						>
							<XIcon stroke="#737373" />
							<span className="sr-only">Close</span>
						</DialogPrimitive.Close>
					</div>
				</div>

				<div className="mt-4 flex min-h-0 flex-1 flex-col gap-5 overflow-hidden px-4 pb-4 sm:min-h-[420px] sm:flex-row sm:gap-3">
					<div className="w-full shrink-0 overflow-x-hidden overflow-y-auto scrollbar-thin sm:w-[200px] sm:pr-1">
						<div className="grid grid-cols-2 gap-1 sm:flex sm:flex-col">
							{categories.map((category) => {
								const isActive = activeCategory === category.id
								return (
									<button
										key={category.id}
										type="button"
										onClick={() => setActiveCategory(category.id)}
										className={cn(
											"flex min-w-0 items-center gap-2.5 px-3 py-2 rounded-[12px] text-left transition-colors cursor-pointer focus:outline-none focus:ring-0 sm:w-full",
											isActive
												? "bg-[#14161A] shadow-inside-out text-[#fafafa]"
												: "text-[#A1A1AA] hover:bg-[#14161A]/50 hover:text-[#fafafa]",
											dmSansClassName(),
										)}
									>
										<span className="shrink-0 w-5 h-5 flex items-center justify-center">
											{category.id === "all" ? (
												<LayoutGrid
													className={cn(
														"size-4",
														isActive ? "text-[#fafafa]" : "text-[#737373]",
													)}
												/>
											) : category.id === "plugin:agents" ? (
												<Logo className="h-[18px] w-[22px]" />
											) : category.iconSrc ? (
												<Image
													src={category.iconSrc}
													alt=""
													width={18}
													height={18}
													className="rounded-[3px]"
													aria-hidden
												/>
											) : category.emoji ? (
												<SpaceGlyph emoji={category.emoji} size={16} />
											) : category.id.startsWith("plugin:") ? (
												<span
													className="w-[18px] h-[18px] flex items-center justify-center rounded-[3px] bg-[#1E232B] text-[#FAFAFA] text-[10px] font-semibold uppercase"
													aria-hidden
												>
													{pluginInitial(category.label)}
												</span>
											) : (
												<FolderIcon
													className={cn(
														"size-4",
														isActive ? "text-[#fafafa]" : "text-[#737373]",
													)}
												/>
											)}
										</span>
										<span className="flex-1 min-w-0 truncate text-[14px] font-medium">
											{category.label}
										</span>
										<span className="shrink-0 text-[11px] text-[#737373] tabular-nums">
											{category.count}
										</span>
									</button>
								)
							})}

							{discoverCategories.length > 0 && (
								<>
									<div className="col-span-2 mt-3 px-3 pt-2 pb-1 text-[10px] uppercase tracking-[0.08em] text-[#737373] sm:mt-2 sm:px-3 sm:pt-2 sm:pb-1">
										Discover
									</div>
									{discoverCategories.map((category) => {
										const isActive = activeCategory === category.id
										return (
											<button
												key={category.id}
												type="button"
												onClick={() => setActiveCategory(category.id)}
												className={cn(
													"flex min-w-0 items-center gap-2.5 px-3 py-2 rounded-[12px] text-left transition-colors cursor-pointer focus:outline-none focus:ring-0 sm:w-full",
													isActive
														? "bg-[#14161A] shadow-inside-out text-[#fafafa] opacity-100"
														: "opacity-55 hover:opacity-100 hover:bg-[#14161A]/50 text-[#A1A1AA] hover:text-[#fafafa]",
													dmSansClassName(),
												)}
											>
												<span className="shrink-0 w-5 h-5 flex items-center justify-center">
													{category.id === "discover:agents" ? (
														<Logo className="h-[18px] w-[22px]" />
													) : category.iconSrc ? (
														<Image
															src={category.iconSrc}
															alt=""
															width={18}
															height={18}
															className="rounded-[3px]"
															aria-hidden
														/>
													) : (
														<span
															className="w-[18px] h-[18px] flex items-center justify-center rounded-[3px] bg-[#1E232B] text-[#FAFAFA] text-[10px] font-semibold uppercase"
															aria-hidden
														>
															{pluginInitial(category.label)}
														</span>
													)}
												</span>
												<span className="flex-1 min-w-0 truncate text-[14px] font-medium">
													{category.label}
												</span>
												<ArrowRight className="size-3.5 shrink-0 text-[#737373]" />
											</button>
										)
									})}
								</>
							)}
						</div>
					</div>

					<div className="flex min-h-0 min-w-0 flex-1 flex-col gap-3 overflow-hidden">
						{activeCategory.startsWith("discover:") ? (
							discoverPanelContent
						) : (
							<>
								<div className="relative">
									<Search className="absolute left-3 top-1/2 -translate-y-1/2 size-4 text-[#737373]" />
									<input
										type="text"
										value={searchQuery}
										onChange={(e) => setSearchQuery(e.target.value)}
										placeholder="Search spaces..."
										className={cn(
											"w-full bg-[#14161A] shadow-inside-out pl-10 pr-4 py-2.5 rounded-[12px] text-[#fafafa] text-[14px] placeholder:text-[#737373] focus:outline-none",
											dmSansClassName(),
										)}
										autoFocus
									/>
								</div>

								<div className="min-h-0 flex-1 overflow-x-hidden overflow-y-auto scrollbar-thin pr-1 sm:max-h-[360px]">
									{filteredProjects.length === 0 ? (
										<p className="text-center text-[#737373] text-sm py-8">
											No spaces found
										</p>
									) : (
										<div className="flex flex-col gap-1">
											{showAutoRow && (
												<>
													<div className="px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
														Mode
													</div>
													{renderAutoRow()}
													<div className="my-1.5 h-px bg-[rgba(82,89,102,0.18)]" />
												</>
											)}
											{recentProjects.length > 0 && (
												<>
													<div className="flex items-center gap-1.5 px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
														<Clock className="size-3" />
														Recently used
													</div>
													{recentProjects.map(renderRow)}
													<div className="my-1.5 h-px bg-[rgba(82,89,102,0.18)]" />
													<div className="px-3 pt-0.5 pb-0.5 text-[10px] uppercase tracking-[0.08em] text-[#737373]">
														All spaces
													</div>
												</>
											)}
											{mainList.map(renderRow)}
										</div>
									)}
								</div>
							</>
						)}
					</div>
				</div>

				{!activeCategory.startsWith("discover:") &&
					(isBulkDeleteMode || (showNewSpace && onNewSpace)) && (
						<div className="flex items-center justify-between gap-3 border-t border-[rgba(82,89,102,0.18)] px-4 py-3">
							{isBulkDeleteMode ? (
								<>
									<p className="min-w-0 text-[13px] font-medium text-[#737373]">
										{bulkDeleteCount === 0
											? "No spaces selected"
											: `${bulkDeleteCount} ${
													bulkDeleteCount === 1 ? "space" : "spaces"
												} selected`}
									</p>
									<div className="flex shrink-0 items-center gap-2">
										<button
											type="button"
											onClick={handleBulkModeToggle}
											className={cn(
												"px-3 py-2 text-[13px] font-medium text-[#737373] transition-colors hover:text-[#fafafa]",
												dmSansClassName(),
											)}
										>
											Cancel
										</button>
										<button
											type="button"
											disabled={bulkDeleteCount === 0}
											onClick={() => {
												if (bulkDeleteCount === 0) return
												onBulkDeleteRequest?.(bulkDeleteProjects)
												setIsBulkDeleteMode(false)
												setBulkDeleteTags(new Set())
												setLastBulkDeleteTag(null)
											}}
											className={cn(
												"flex items-center gap-2 rounded-full bg-red-600 px-4 py-2 text-[13px] font-medium text-white transition-colors hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-40",
												dmSansClassName(),
											)}
										>
											<Trash2 className="size-4" />
											Delete selected
										</button>
									</div>
								</>
							) : (
								<>
									<span />
									{showNewSpace && onNewSpace && (
										<button
											type="button"
											onClick={onNewSpace}
											className={cn(
												"flex items-center gap-2 px-4 py-2 rounded-full text-[13px] font-medium text-[#fafafa] bg-[#14161A] shadow-inside-out hover:bg-[#121820] transition-colors cursor-pointer focus:outline-none focus:ring-0",
												dmSansClassName(),
											)}
										>
											<Plus className="size-4" />
											New space
										</button>
									)}
								</>
							)}
						</div>
					)}
			</DialogContent>
		</Dialog>
	)
}

function AgentsDiscoverPanel({
	catalogIds,
	connectingPluginId,
	newKey,
	onConnect,
	onDismissKey,
}: {
	catalogIds: readonly string[]
	connectingPluginId: string | null
	newKey: { pluginId: string; key: string } | null
	onConnect: (catalogId: string) => void
	onDismissKey: () => void
}) {
	const [activeCatalogId, setActiveCatalogId] = useState(
		newKey?.pluginId ?? catalogIds[0] ?? "codex",
	)

	// snap to the just-connected plugin only when the key changes, so manual tab clicks aren't overridden
	useEffect(() => {
		if (newKey?.pluginId) setActiveCatalogId(newKey.pluginId)
	}, [newKey?.pluginId])

	useEffect(() => {
		if (!catalogIds.includes(activeCatalogId)) {
			setActiveCatalogId(catalogIds[0] ?? "codex")
		}
	}, [activeCatalogId, catalogIds])

	if (catalogIds.length === 0) {
		return (
			<p className="py-8 text-center text-sm text-[#737373]">
				Claude Code, Codex, and OpenCode are connected.
			</p>
		)
	}

	return (
		<div className="flex min-h-0 flex-1 flex-col gap-4 overflow-hidden">
			<div className="scrollbar-none flex shrink-0 items-center gap-1.5 overflow-x-auto border-b border-white/[0.06] pb-3">
				{catalogIds.map((catalogId) => {
					const info = PLUGIN_CATALOG[catalogId]
					if (!info) return null
					const isActive = activeCatalogId === catalogId
					return (
						<button
							key={catalogId}
							type="button"
							onClick={() => setActiveCatalogId(catalogId)}
							className={cn(
								"flex shrink-0 items-center gap-2 whitespace-nowrap rounded-full border px-3 py-2 transition-colors",
								isActive
									? "border-[#2261CA33] bg-[#00173C] text-[#fafafa]"
									: "border-[#161F2C] bg-[#0D121A] text-[#A1A1AA] hover:text-[#FAFAFA]",
							)}
						>
							<Image src={info.icon} alt="" width={16} height={16} />
							<span className="text-[12px] font-medium">{info.name}</span>
						</button>
					)
				})}
			</div>
			<DiscoverPanel
				catalogId={activeCatalogId}
				hideHeader
				isConnecting={connectingPluginId === activeCatalogId}
				newKey={newKey?.pluginId === activeCatalogId ? newKey.key : null}
				onConnect={() => onConnect(activeCatalogId)}
				onDismissKey={onDismissKey}
			/>
		</div>
	)
}

function DiscoverPanel({
	catalogId,
	isConnecting,
	newKey,
	onConnect,
	onDismissKey,
	hideHeader,
}: {
	catalogId: string
	isConnecting: boolean
	newKey: string | null
	onConnect: () => void
	onDismissKey: () => void
	hideHeader?: boolean
}) {
	const info = PLUGIN_CATALOG[catalogId]
	if (!info) {
		return (
			<p className="text-[#737373] text-sm py-8 text-center">
				Plugin info unavailable.
			</p>
		)
	}

	const pluginSteps = info.installSteps ?? []
	const stepsEmbedKey = pluginSteps.some((s) => s.code?.includes("sm_..."))
	const setupSteps = stepsEmbedKey
		? pluginSteps
		: [
				{
					title: "Copy your API key",
					description:
						"You won't be able to see it again — store it somewhere safe.",
					code: newKey ?? "sm_...",
					copyLabel: "API key",
					secret: true,
				},
				...pluginSteps,
			]
	const isConnected = !!newKey

	return (
		<div className="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto scrollbar-thin pr-1">
			{hideHeader ? (
				<p className="text-[13px] leading-[1.4] text-[#737373]">
					{info.tagline}
				</p>
			) : (
				<div className="flex items-center gap-3">
					<div className="flex size-10 shrink-0 items-center justify-center rounded-[10px] border border-[#1E293B] bg-[#080B0F]">
						<Image
							alt={info.name}
							className="size-[22px]"
							height={22}
							src={info.icon}
							width={22}
						/>
					</div>
					<div className="flex-1 min-w-0">
						<p
							className={cn(
								dmSans125ClassName(),
								"font-semibold text-[15px] text-[#FAFAFA]",
							)}
						>
							{info.name}
						</p>
						<p className="text-[12px] text-[#737373] leading-[1.4]">
							{info.tagline}
						</p>
					</div>
				</div>
			)}

			{isConnected && (
				<div className="flex items-center justify-between gap-2">
					<p className="text-[13px] font-medium text-[#FAFAFA]">
						Plugin connected — finish setup
					</p>
					<button
						type="button"
						onClick={onDismissKey}
						className="text-[#737373] hover:text-[#FAFAFA] cursor-pointer"
						aria-label="Dismiss"
					>
						<XIcon className="size-4" />
					</button>
				</div>
			)}

			{isConnected && (
				<p className="text-[12px] text-[#737373]">
					Your API key is shown once. Hover or focus the blurred command to
					reveal it.
				</p>
			)}

			{isConnected ? (
				<InstallSteps steps={setupSteps} apiKey={newKey ?? undefined} />
			) : (
				<>
					<div className="flex items-center gap-2">
						<PillButton onClick={onConnect} disabled={isConnecting}>
							{isConnecting ? (
								<>
									<Loader className="size-3.5 animate-spin" /> Connecting…
								</>
							) : (
								`Connect ${info.name}`
							)}
						</PillButton>
						{info.docsUrl && (
							<a
								href={info.docsUrl}
								target="_blank"
								rel="noopener noreferrer"
								className={cn(
									dmSans125ClassName(),
									"flex h-8 items-center justify-center gap-1.5 rounded-full px-3 sm:h-9 sm:px-4",
									"text-[12px] font-medium text-[#A1A1AA] sm:text-[14px]",
									"transition-colors hover:text-[#FAFAFA]",
								)}
							>
								<BookOpen className="size-3.5" /> Docs
							</a>
						)}
					</div>
					<div className="space-y-2.5">
						<p className="text-[11px] font-semibold uppercase tracking-wide text-[#737373]">
							What you'll do next
						</p>
						<ol className="flex flex-col gap-2">
							{setupSteps.map((step, i) => (
								<li key={step.title} className="flex items-center gap-2.5">
									<span
										className={cn(
											"flex size-[22px] shrink-0 items-center justify-center rounded-full bg-[#0D121A] text-[11px] font-semibold text-[#4BA0FA]",
											INSET,
										)}
									>
										{i + 1}
									</span>
									<span className="text-[13px] text-[#A1A1AA]">
										{step.title}
									</span>
								</li>
							))}
						</ol>
					</div>
				</>
			)}
		</div>
	)
}
