"use client"

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import { useCustomer } from "autumn-js/react"
import { cn } from "@lib/utils"
import { dmSans125ClassName } from "@/lib/fonts"
import { hasActivePlan } from "@lib/queries"
import { $fetch } from "@lib/api"
import { authClient } from "@lib/auth"
import { useAuth } from "@lib/auth-context"
import type { ConnectionResponseSchema } from "@repo/validation/api"
import type { z } from "zod"
import { Button } from "@ui/components/button"
import {
	ChromeIcon,
	AppleShortcutsIcon,
	RaycastIcon,
} from "@/components/integration-icons"
import { GoogleDrive, Notion, OneDrive, MCPIcon } from "@ui/assets/icons"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import {
	ArrowLeft,
	ArrowRight,
	BookOpen,
	Check,
	Loader,
	Plus,
	Search,
	X,
	Zap,
} from "lucide-react"
import { formatRelativeTime } from "@/components/settings/sync-utils"
import { CHROME_EXTENSION_URL } from "@lib/constants"
import { analytics } from "@/lib/analytics"
import Image from "next/image"
import { useViewMode } from "@/lib/view-mode-context"
import type { ViewParamValue } from "@/lib/search-params"
import { parseAsString, parseAsStringEnum, useQueryState } from "nuqs"
import { addDocumentParam } from "@/lib/search-params"
import {
	useCallback,
	useEffect,
	useMemo,
	useRef,
	useState,
	type ReactNode,
} from "react"
import { AnimatePresence, motion } from "motion/react"
import { toast } from "sonner"
import { Dialog, DialogContent, DialogTitle } from "@ui/components/dialog"
import {
	PLUGIN_CATALOG,
	FREE_TIER_PLUGIN_IDS,
	isFreeTierPlugin,
	normalizePluginClientId,
	type InstallStep,
} from "@/lib/plugin-catalog"
import { INSET, InstallSteps, PillButton } from "./integrations/install-steps"
import { MCPSteps } from "./mcp-modal/mcp-detail-view"

type Connection = z.infer<typeof ConnectionResponseSchema>

type ConnectorProvider = "google-drive" | "notion" | "onedrive"

interface ConnectedKey {
	keyId: string
	keyStart: string | null
	pluginId: string
	lastRequest?: string | null
	createdAt?: string | null
}

function toIsoDate(value: string | Date | null | undefined): string | null {
	if (!value) return null
	const d = value instanceof Date ? value : new Date(value)
	if (Number.isNaN(d.getTime())) return null
	return d.toISOString()
}

function parsePluginAuthKeys(
	apiKeys: ListedApiKey[],
	keyPrefix: (key: ListedApiKey) => string | null,
): { active: ConnectedKey[]; setup: ConnectedKey[] } {
	const active: ConnectedKey[] = []
	const setup: ConnectedKey[] = []
	for (const key of apiKeys) {
		if (key.enabled === false) continue
		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) continue
			const entry: ConnectedKey = {
				keyId: key.id,
				keyStart: keyPrefix(key),
				pluginId: normalizePluginClientId(metadata.sm_client),
				lastRequest: toIsoDate(key.lastRequest),
				createdAt: toIsoDate(key.createdAt),
			}
			if (key.lastRequest) active.push(entry)
			else setup.push(entry)
		} catch {}
	}
	return { active, setup }
}

type ListedApiKey = {
	id: string
	name?: string | null
	createdAt?: string | Date | null
	enabled?: boolean
	lastRequest?: string | Date | null
	metadata: string | Record<string, unknown> | null
	start?: string | null
}

type ItemKind = "plugin" | "connector" | "client" | "mcp-client" | "import"

type MCPClientKey =
	| "chatgpt"
	| "codex"
	| "cursor"
	| "claude"
	| "vscode"
	| "cline"
	| "gemini-cli"
	| "claude-code"
	| "mcp-url"

const MCP_CLIENTS: Array<{
	key: MCPClientKey
	name: string
	tagline: string
	simpleTitle?: string
	dev?: boolean
}> = [
	{
		key: "cursor",
		name: "Cursor",
		tagline: "One-click MCP install in Cursor",
		simpleTitle: "Code with your saved knowledge nearby",
	},
	{
		key: "claude",
		name: "Claude Desktop",
		tagline: "Connect supermemory in Claude Desktop",
		simpleTitle: "Reference your notes during any Claude chat",
	},
	{
		key: "chatgpt",
		name: "ChatGPT",
		tagline: "Apps via ChatGPT developer mode",
		simpleTitle: "Let ChatGPT recall what you've saved",
	},
	{
		key: "vscode",
		name: "VS Code",
		tagline: "Native MCP support in VS Code",
		simpleTitle: "Pull your knowledge into VS Code while coding",
		dev: true,
	},
	{
		key: "cline",
		name: "Cline",
		tagline: "MCP via the Cline VS Code extension",
		simpleTitle: "Cline can read and add to your memory",
		dev: true,
	},
	{
		key: "gemini-cli",
		name: "Gemini CLI",
		tagline: "Google Gemini terminal client",
		simpleTitle: "Bring your memory into Gemini sessions",
		dev: true,
	},
	{
		key: "codex",
		name: "Codex (MCP)",
		tagline: "OpenAI Codex CLI via MCP config",
		simpleTitle: "Codex with access to your saved context",
		dev: true,
	},
	{
		key: "claude-code",
		name: "Claude Code (MCP)",
		tagline: "Connect via Claude Code MCP config",
		simpleTitle: "Claude Code with your project context",
		dev: true,
	},
	{
		key: "mcp-url",
		name: "MCP URL",
		tagline: "Use the URL in any custom MCP client",
		simpleTitle: "Connect any MCP-capable app to supermemory",
		dev: true,
	},
]

function mcpClientIconSrc(key: MCPClientKey): string {
	if (key === "mcp-url") return "/mcp-icon.svg"
	const file = key === "claude-code" ? "claude" : key
	return `/mcp-supported-tools/${file}.png`
}

const PLUGIN_SIMPLE_TITLES: Record<string, string> = {
	claude_code: "Remembers your code conventions and decisions",
	codex: "Codex sessions that remember your project",
	opencode: "OpenCode with persistent project memory",
	openclaw: "Save chats from Telegram, Discord and Slack",
	hermes: "Persistent memory for the Hermes agent",
}

type CategoryFilter =
	| "all"
	| "connected"
	| "plugins"
	| "knowledge-bases"
	| "apps-extensions"
	| "ai-clients"

const CATEGORY_VALUES: readonly CategoryFilter[] = [
	"all",
	"connected",
	"ai-clients",
	"plugins",
	"knowledge-bases",
	"apps-extensions",
] as const

const catParam = parseAsStringEnum<CategoryFilter>([
	...CATEGORY_VALUES,
]).withDefault("all")

const CATEGORY_LABEL: Record<CategoryFilter, string> = {
	all: "All",
	connected: "Active",
	plugins: "Plugins",
	"knowledge-bases": "Knowledge bases",
	"apps-extensions": "Apps & extensions",
	"ai-clients": "MCP",
}

const SECTION_ORDER: Array<Exclude<CategoryFilter, "all" | "connected">> = [
	"ai-clients",
	"plugins",
	"knowledge-bases",
	"apps-extensions",
]

function itemCategory(
	item: Item,
): Exclude<CategoryFilter, "all" | "connected"> {
	switch (item.kind) {
		case "plugin":
			return "plugins"
		case "connector":
			return "knowledge-bases"
		case "mcp-client":
			return "ai-clients"
		case "client":
		case "import":
			return "apps-extensions"
	}
}

interface BaseItem {
	id: string
	name: string
	tagline: string
	icon: ReactNode
	docsUrl?: string
	pro?: boolean
	kind: ItemKind
	simpleTitle?: string
	dev?: boolean
}

interface PluginItem extends BaseItem {
	kind: "plugin"
	pluginId: string
}

interface ConnectorItem extends BaseItem {
	kind: "connector"
	provider: ConnectorProvider
}

interface ClientItem extends BaseItem {
	kind: "client"
	action:
		| { type: "external"; href: string }
		| { type: "view"; viewMode: ViewParamValue }
}

interface MCPClientItem extends BaseItem {
	kind: "mcp-client"
	clientKey: MCPClientKey
}

interface ImportItem extends BaseItem {
	kind: "import"
	viewMode: ViewParamValue
}

type Item = PluginItem | ConnectorItem | ClientItem | MCPClientItem | ImportItem

const SECTIONS: Array<{
	label: string
	items: (plugin: typeof PLUGIN_CATALOG) => Item[]
}> = [
	{
		label: "MCP",
		items: () =>
			MCP_CLIENTS.map<MCPClientItem>((c) => ({
				kind: "mcp-client",
				clientKey: c.key,
				id: `mcp-${c.key}`,
				name: c.name,
				tagline: c.tagline,
				simpleTitle: c.simpleTitle,
				dev: c.dev,
				icon:
					c.key === "mcp-url" ? (
						<MCPIcon className="size-6" />
					) : (
						<Image
							src={mcpClientIconSrc(c.key)}
							alt={c.name}
							width={24}
							height={24}
							unoptimized
							className="size-6 rounded object-contain"
						/>
					),
				docsUrl: "https://docs.supermemory.ai/supermemory-mcp/introduction",
			})),
	},
	{
		label: "Plugins",
		items: (catalog) =>
			Object.keys(catalog).map<PluginItem>((id) => {
				const plugin = catalog[id]
				if (!plugin) throw new Error(`Missing plugin ${id}`)
				return {
					kind: "plugin",
					id: `plugin-${id}`,
					pluginId: id,
					name: plugin.name,
					tagline: plugin.tagline,
					icon: (
						<Image
							src={plugin.icon}
							alt={plugin.name}
							width={24}
							height={24}
							className="size-6 rounded"
						/>
					),
					docsUrl: plugin.docsUrl,
					pro: !FREE_TIER_PLUGIN_IDS.includes(id),
					simpleTitle: PLUGIN_SIMPLE_TITLES[id],
					dev: true,
				}
			}),
	},
	{
		label: "Knowledge bases",
		items: () => [
			{
				kind: "connector",
				id: "google-drive",
				provider: "google-drive",
				name: "Google Drive",
				tagline: "Sync Docs, Sheets and Slides into your memory",
				simpleTitle: "Your Docs, Sheets and Slides, searchable",
				icon: <GoogleDrive className="size-6" />,
				pro: true,
			},
			{
				kind: "connector",
				id: "notion",
				provider: "notion",
				name: "Notion",
				tagline: "Import Notion pages and databases",
				simpleTitle: "All your Notion pages, in supermemory",
				icon: <Notion className="size-6" />,
				pro: true,
			},
			{
				kind: "connector",
				id: "onedrive",
				provider: "onedrive",
				name: "OneDrive",
				tagline: "Bring in Office documents from OneDrive",
				simpleTitle: "Your OneDrive files, ready to recall",
				icon: <OneDrive className="size-6" />,
				pro: true,
			},
		],
	},
	{
		label: "Apps & extensions",
		items: () => [
			{
				kind: "client",
				id: "chrome",
				name: "Chrome Extension",
				tagline: "Save webpages, import bookmarks, sync ChatGPT memories",
				simpleTitle: "Save any webpage with one click",
				icon: <ChromeIcon className="size-6" />,
				action: { type: "external", href: CHROME_EXTENSION_URL },
			},
			{
				kind: "client",
				id: "shortcuts",
				name: "Apple Shortcuts",
				tagline: "Add memories from iPhone, iPad or Mac",
				simpleTitle: "Save anything from your phone or Mac",
				icon: <AppleShortcutsIcon />,
				action: { type: "view", viewMode: "shortcuts" as ViewParamValue },
			},
			{
				kind: "client",
				id: "raycast",
				name: "Raycast",
				tagline: "Add and search memories from Raycast on Mac",
				simpleTitle: "Save and search from Raycast on Mac",
				icon: <RaycastIcon className="size-6" />,
				action: { type: "view", viewMode: "raycast" as ViewParamValue },
				dev: true,
			},
			{
				kind: "import",
				id: "x-bookmarks",
				name: "Import X bookmarks",
				tagline: "Turn your X/Twitter bookmarks into memories",
				simpleTitle: "Turn your X bookmarks into memory",
				icon: <Image src="/onboarding/x.png" alt="X" width={24} height={24} />,
				viewMode: "import" as ViewParamValue,
			},
		],
	},
]

export function DetailWrapper({
	onBack,
	children,
}: {
	onBack: () => void
	children: ReactNode
}) {
	return (
		<div className="flex-1 p-4 md:p-6 pt-2">
			<div className="max-w-3xl mx-auto">
				<Button
					variant="link"
					className="text-white hover:text-gray-300 p-0 hover:no-underline cursor-pointer mb-4"
					onClick={onBack}
				>
					<ArrowLeft className="size-4 mr-1" />
					Back to Integrations
				</Button>
				{children}
			</div>
		</div>
	)
}

function ProChip() {
	return (
		<span
			className={cn(
				dmSans125ClassName(),
				"ml-auto shrink-0 pl-2 text-[10px] font-semibold uppercase tracking-wide text-[#4BA0FA]",
			)}
		>
			Pro
		</span>
	)
}

function IconBox({ children }: { children: ReactNode }) {
	return (
		<div
			className={cn(
				"flex size-10 shrink-0 items-center justify-center rounded-[10px] bg-[#080B0F]",
				"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.6)]",
			)}
		>
			{children}
		</div>
	)
}

function DocsLink({ href }: { href: string }) {
	return (
		<a
			aria-label="Open docs"
			href={href}
			target="_blank"
			rel="noopener noreferrer"
			onClick={(e) => e.stopPropagation()}
			className={cn(
				dmSans125ClassName(),
				"flex size-8 shrink-0 items-center justify-center rounded-full text-[12px] text-[#A1A1AA] transition-colors hover:text-white sm:h-auto sm:w-auto sm:gap-1.5 sm:rounded-none",
			)}
		>
			<BookOpen className="size-3.5" />
			<span className="hidden sm:inline">Docs</span>
		</a>
	)
}

function DisconnectButton({ onConfirm }: { onConfirm: () => void }) {
	const [confirming, setConfirming] = useState(false)
	useEffect(() => {
		if (!confirming) return
		const t = setTimeout(() => setConfirming(false), 3000)
		return () => clearTimeout(t)
	}, [confirming])
	return (
		<button
			type="button"
			onClick={() => (confirming ? onConfirm() : setConfirming(true))}
			className={cn(
				dmSans125ClassName(),
				"shrink-0 rounded-md px-2 py-1 text-[11px] font-medium transition-colors",
				confirming
					? "bg-red-500/15 text-red-400"
					: "text-[#737373] hover:bg-white/5 hover:text-red-400",
			)}
		>
			{confirming ? "Confirm" : "Disconnect"}
		</button>
	)
}

function ActiveButton({
	count,
	lastActive,
	onClick,
}: {
	count: number
	lastActive?: string | null
	onClick: () => void
}) {
	return (
		<button
			type="button"
			onClick={onClick}
			className={cn(
				dmSans125ClassName(),
				"group flex shrink-0 cursor-pointer items-center gap-1.5 text-[12px] font-medium text-[#00AC3F] transition-colors sm:text-[13px]",
			)}
		>
			<span className="size-[7px] rounded-full bg-[#00AC3F]" />
			<span className="group-hover:underline">
				{count > 1 ? `${count} active` : "Active"}
			</span>
			{count <= 1 && lastActive && (
				<span className="text-[11px] font-normal text-[#737373]">
					· {formatRelativeTime(lastActive)}
				</span>
			)}
		</button>
	)
}

function FinishSetupButton({ onClick }: { onClick: () => void }) {
	return (
		<PillButton onClick={onClick}>
			<span className="size-[7px] rounded-full bg-[#EAB308]" />
			Finish setup
		</PillButton>
	)
}

function ConnectionsCountPill({ count }: { count: number }) {
	return (
		<span
			className={cn(
				dmSans125ClassName(),
				"flex shrink-0 items-center gap-1.5 text-[12px] font-medium text-[#00AC3F] sm:text-[13px]",
			)}
		>
			<span className="size-[7px] rounded-full bg-[#00AC3F]" />
			{count > 1 ? `${count} connected` : "Connected"}
		</span>
	)
}

function ItemCard({
	icon,
	name,
	tagline,
	pro,
	docsUrl,
	leftIndicator,
	rightSlot,
}: {
	icon: ReactNode
	name: string
	tagline: string
	pro?: boolean
	docsUrl?: string
	leftIndicator?: ReactNode
	rightSlot: ReactNode
}) {
	return (
		<div
			className={cn(
				"flex h-full flex-col gap-4 rounded-[12px] bg-[#14161A] p-4 transition-colors hover:bg-[#16181D]",
				"shadow-[inset_2.42px_2.42px_4.263px_rgba(11,15,21,0.7)]",
			)}
		>
			<div className="flex items-start justify-between gap-2">
				<IconBox>{icon}</IconBox>
				{docsUrl && (
					// biome-ignore lint/a11y/noStaticElementInteractions: wrapper to stop event propagation
					<div
						role="presentation"
						onClick={(e) => e.stopPropagation()}
						onKeyDown={(e) => e.stopPropagation()}
					>
						<DocsLink href={docsUrl} />
					</div>
				)}
			</div>
			<div className="flex flex-1 flex-col justify-end gap-3">
				<div className="min-w-0">
					<div className="flex min-w-0 items-center gap-1">
						{leftIndicator}
						<span
							className={cn(
								dmSans125ClassName(),
								"min-w-0 truncate text-[14px] font-medium text-[#FAFAFA]",
							)}
						>
							{name}
						</span>
						{pro && <ProChip />}
					</div>
					<p
						className={cn(
							dmSans125ClassName(),
							"mt-1 line-clamp-2 text-[12px] leading-snug text-[#A1A1AA]",
						)}
					>
						{tagline}
					</p>
				</div>
				<div className="flex justify-end">{rightSlot}</div>
			</div>
		</div>
	)
}

interface FeaturedPick {
	id: string
	name: string
	headline: string
	support: string
	tagline: string
	icon: ReactNode
	backdrop?: ReactNode
	docsUrl?: string
	ctaLabel: string
	onCta: () => void
}

const FEATURED_ROTATE_MS = 7000

function FeaturedHero({ picks }: { picks: FeaturedPick[] }) {
	const [index, setIndex] = useState(0)
	const [paused, setPaused] = useState(false)
	const [manualOverride, setManualOverride] = useState(false)

	useEffect(() => {
		if (picks.length <= 1) return
		if (paused || manualOverride) return
		const t = setInterval(() => {
			setIndex((i) => (i + 1) % picks.length)
		}, FEATURED_ROTATE_MS)
		return () => clearInterval(t)
	}, [picks.length, paused, manualOverride])

	if (picks.length === 0) return null
	const pick = picks[index] ?? picks[0]
	if (!pick) return null

	return (
		<button
			type="button"
			onClick={pick.onCta}
			onMouseEnter={() => setPaused(true)}
			onMouseLeave={() => setPaused(false)}
			onFocus={() => setPaused(true)}
			onBlur={() => setPaused(false)}
			className={cn(
				"group relative w-full overflow-hidden rounded-[14px] px-5 py-5 sm:px-6 sm:py-6 text-left cursor-pointer",
				"bg-[#191D24]",
				"shadow-[inset_2.42px_2.42px_4.263px_rgba(11,15,21,0.7)]",
				"min-h-[160px]",
			)}
		>
			{pick.backdrop && (
				<AnimatePresence mode="wait">
					<motion.div
						key={`bg-${pick.id}`}
						initial={{ opacity: 0, x: 12 }}
						animate={{ opacity: 1, x: 0 }}
						exit={{ opacity: 0, x: 8 }}
						transition={{ duration: 0.45, ease: [0.4, 0, 0.2, 1] }}
						aria-hidden
						className="pointer-events-none absolute inset-y-0 right-0 w-3/5 sm:w-2/3"
					>
						<div className="absolute -right-8 -top-10 sm:right-0 sm:-top-4 opacity-55">
							{pick.backdrop}
						</div>
						<div className="absolute inset-0 bg-gradient-to-l from-transparent via-[#191D24]/40 to-[#191D24]" />
						<div className="absolute inset-0 bg-gradient-to-t from-[#191D24]/60 via-transparent to-transparent" />
					</motion.div>
				</AnimatePresence>
			)}
			<div
				aria-hidden
				className="pointer-events-none absolute inset-0 opacity-[0.06] mix-blend-overlay"
				style={{
					backgroundImage:
						"url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E\")",
				}}
			/>

			<span
				className={cn(
					dmSans125ClassName(),
					"relative z-1 mb-3 inline-flex items-center rounded-full px-2.5 py-1 text-[10px] font-bold uppercase tracking-[0.14em] text-[#06121A]",
					"shadow-[0_0_14px_rgba(54,253,253,0.30)]",
				)}
				style={{
					background:
						"linear-gradient(94deg, #369BFD 4.8%, #36FDFD 77.04%, #36FDB5 143.99%)",
				}}
			>
				Featured
			</span>
			{picks.length > 1 && (
				<div className="absolute right-5 top-5 sm:right-6 sm:top-6 z-1 flex shrink-0 items-center gap-1.5">
					{picks.map((p, i) => {
						const active = i === index
						return (
							<button
								key={p.id}
								type="button"
								aria-label={`Show featured ${p.name}`}
								onClick={(e) => {
									e.stopPropagation()
									setIndex(i)
									setManualOverride(true)
								}}
								className={cn(
									"rounded-full transition-all cursor-pointer",
									active
										? "w-3 h-1 bg-[#4BA0FA]"
										: "size-1 bg-[#2A3040] hover:bg-[#3A4455]",
								)}
							/>
						)
					})}
				</div>
			)}

			<AnimatePresence mode="wait">
				<motion.div
					key={pick.id}
					initial={{ opacity: 0, y: 4 }}
					animate={{ opacity: 1, y: 0 }}
					exit={{ opacity: 0, y: -3 }}
					transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}
					className="relative flex max-w-[55%] flex-col gap-1.5"
				>
					<h3
						className={cn(
							dmSans125ClassName(),
							"text-[18px] font-semibold leading-tight tracking-[-0.15px] text-[#FAFAFA]",
						)}
					>
						{pick.headline}
					</h3>
					<p
						className={cn(
							dmSans125ClassName(),
							"text-[13px] leading-snug text-[#A1A1AA]",
						)}
					>
						<span className="font-medium text-[#CBD5E1]">{pick.name}</span> ·{" "}
						{pick.support}
					</p>
				</motion.div>
			</AnimatePresence>
		</button>
	)
}

function SearchToggle({
	value,
	onChange,
	expanded,
	setExpanded,
}: {
	value: string
	onChange: (next: string) => void
	expanded: boolean
	setExpanded: (next: boolean) => void
}) {
	const inputRef = useRef<HTMLInputElement>(null)

	const open = () => {
		setExpanded(true)
		requestAnimationFrame(() => inputRef.current?.focus())
	}

	const close = () => {
		onChange("")
		setExpanded(false)
	}

	if (!expanded && !value) {
		return (
			<button
				type="button"
				aria-label="Search integrations"
				onClick={open}
				className={cn(
					"flex size-8 shrink-0 items-center justify-center rounded-full bg-[#0D121A] text-[#A1A1AA] transition-colors hover:text-[#FAFAFA]",
					"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.5)]",
				)}
			>
				<Search className="size-3.5" />
			</button>
		)
	}

	return (
		<div
			className={cn(
				"flex h-8 min-w-0 flex-1 items-center gap-1.5 rounded-full bg-[#0D121A] px-3",
				"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.5)]",
			)}
		>
			<Search className="size-3.5 shrink-0 text-[#A1A1AA]" />
			<input
				ref={inputRef}
				type="text"
				value={value}
				onChange={(e) => onChange(e.target.value)}
				onKeyDown={(e) => {
					if (e.key === "Escape") close()
				}}
				onBlur={() => {
					if (!value) setExpanded(false)
				}}
				placeholder="Search integrations"
				className={cn(
					dmSans125ClassName(),
					"min-w-0 flex-1 bg-transparent text-[12px] text-[#FAFAFA] placeholder:text-[#525D6E] focus:outline-none",
				)}
			/>
			{value && (
				<button
					type="button"
					aria-label="Clear search"
					onClick={close}
					className="shrink-0 text-[#737373] transition-colors hover:text-[#FAFAFA]"
				>
					<X className="size-3.5" />
				</button>
			)}
		</div>
	)
}

function CategoryFilterToggle({
	value,
	onChange,
	counts,
	compact,
}: {
	value: CategoryFilter
	onChange: (next: CategoryFilter) => void
	counts: Record<CategoryFilter, number>
	compact?: boolean
}) {
	const visible = compact
		? [value]
		: CATEGORY_VALUES.filter((v) => v === "all" || counts[v] > 0)
	return (
		<div
			className={cn(
				"scrollbar-none flex items-center gap-0.5 overflow-x-auto rounded-full bg-[#0D121A] p-0.5",
				"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.5)]",
			)}
		>
			{visible.map((v) => {
				const active = value === v
				return (
					<button
						key={v}
						type="button"
						onClick={() => onChange(v)}
						className={cn(
							dmSans125ClassName(),
							"flex h-7 shrink-0 items-center gap-1.5 rounded-full px-3 text-[12px] font-medium transition-colors",
							active
								? "bg-white/[0.10] text-[#FAFAFA]"
								: "text-[#A1A1AA] hover:text-[#FAFAFA]",
						)}
					>
						{CATEGORY_LABEL[v]}
						{v !== "all" && (
							<span
								className={cn(
									"text-[10px] font-semibold tabular-nums",
									active ? "text-[#A1A1AA]" : "text-[#525D6E]",
								)}
							>
								{counts[v]}
							</span>
						)}
					</button>
				)
			})}
		</div>
	)
}

function SectionRail({
	label,
	children,
}: {
	label: string
	children: ReactNode
}) {
	const scrollRef = useRef<HTMLDivElement>(null)
	const [canScrollLeft, setCanScrollLeft] = useState(false)
	const [canScrollRight, setCanScrollRight] = useState(false)

	const update = useCallback(() => {
		const el = scrollRef.current
		if (!el) return
		setCanScrollLeft(el.scrollLeft > 4)
		setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 4)
	}, [])

	useEffect(() => {
		update()
		const el = scrollRef.current
		if (!el) return
		el.addEventListener("scroll", update, { passive: true })
		el.addEventListener("scrollend", update)
		const ro = new ResizeObserver(update)
		ro.observe(el)
		return () => {
			el.removeEventListener("scroll", update)
			el.removeEventListener("scrollend", update)
			ro.disconnect()
		}
	}, [update])

	const scrollBy = (dir: 1 | -1) => {
		scrollRef.current?.scrollBy({ left: 292 * dir, behavior: "smooth" })
		setTimeout(update, 450)
	}

	const arrowClass = cn(
		"flex size-7 items-center justify-center rounded-full bg-[#0D121A] text-[#FAFAFA] transition-opacity",
		"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.6)]",
		"hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-30",
	)

	return (
		<section className="flex flex-col gap-3">
			<div className="flex items-center justify-between gap-3">
				<h3
					className={cn(
						dmSans125ClassName(),
						"text-[13px] font-semibold tracking-[-0.01em] text-[#A1A1AA]",
					)}
				>
					{label}
				</h3>
				<div className="flex items-center gap-1.5">
					<button
						type="button"
						aria-label="Show previous"
						disabled={!canScrollLeft}
						onClick={() => scrollBy(-1)}
						className={arrowClass}
					>
						<ArrowLeft className="size-3.5" />
					</button>
					<button
						type="button"
						aria-label="Show more"
						disabled={!canScrollRight}
						onClick={() => scrollBy(1)}
						className={arrowClass}
					>
						<ArrowRight className="size-3.5" />
					</button>
				</div>
			</div>
			<div
				ref={scrollRef}
				className="scrollbar-none flex gap-3 overflow-x-auto -mx-1 px-1"
			>
				{children}
			</div>
		</section>
	)
}

export function IntegrationsView() {
	const { setViewMode } = useViewMode()
	const queryClient = useQueryClient()
	const { org } = useAuth()
	const autumn = useCustomer()
	const hasProProduct = hasActivePlan(autumn.data?.subscriptions, "api_pro")
	const isAutumnLoading = autumn.isLoading

	const [connectingPlugin, setConnectingPlugin] = useState<string | null>(null)
	const [connectingProvider, setConnectingProvider] =
		useState<ConnectorProvider | null>(null)
	const [newKey, setNewKey] = useState<{
		open: boolean
		key: string
		pluginId: string | null
	}>({ open: false, key: "", pluginId: null })
	const [connectedPluginId, setConnectedPluginId] = useState<string | null>(
		null,
	)
	const [finishSetupPluginId, setFinishSetupPluginId] = useState<string | null>(
		null,
	)

	const { data: pluginsData } = useQuery({
		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[] }
		},
		queryKey: ["plugins"],
	})

	const { data: connections = [] } = useQuery({
		queryKey: ["connections"],
		queryFn: async () => {
			const response = await $fetch("@post/connections/list", {
				body: { containerTags: [] },
			})
			if (response.error)
				throw new Error(response.error?.message || "Failed to load connections")
			return response.data as Connection[]
		},
		staleTime: 30 * 1000,
		enabled: hasProProduct,
	})

	const { data: apiKeys = [], refetch: refetchKeys } = useQuery<ListedApiKey[]>(
		{
			queryKey: ["api-keys", org?.id],
			queryFn: async () => {
				if (!org?.id) return []
				const API_URL =
					process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai"
				const res = await fetch(`${API_URL}/v3/auth/keys`, {
					credentials: "include",
				})
				if (!res.ok) return []
				const data = (await res.json()) as { keys?: ListedApiKey[] }
				return data.keys ?? []
			},
			enabled: !!org?.id,
			staleTime: 30 * 1000,
		},
	)

	const keyPrefix = useCallback((key: ListedApiKey): string | null => {
		return key.start ?? (key.name?.startsWith("sm_") ? key.name : null)
	}, [])

	const { active: activePlugins, setup: setupPlugins } = useMemo(
		() => parsePluginAuthKeys(apiKeys, keyPrefix),
		[apiKeys, keyPrefix],
	)

	const activePluginById = useMemo(() => {
		const map = new Map<string, ConnectedKey>()
		for (const key of activePlugins) {
			const existing = map.get(key.pluginId)
			if (!existing) {
				map.set(key.pluginId, key)
				continue
			}
			const a = key.lastRequest ? new Date(key.lastRequest).getTime() : 0
			const b = existing.lastRequest
				? new Date(existing.lastRequest).getTime()
				: 0
			if (a >= b) map.set(key.pluginId, key)
		}
		return map
	}, [activePlugins])

	const activeCountByPlugin = useMemo(() => {
		const map = new Map<string, number>()
		for (const key of activePlugins) {
			map.set(key.pluginId, (map.get(key.pluginId) ?? 0) + 1)
		}
		return map
	}, [activePlugins])

	const setupPluginIds = useMemo(
		() => new Set(setupPlugins.map((k) => k.pluginId)),
		[setupPlugins],
	)

	const connectionsByProvider = useMemo(() => {
		const out: Record<ConnectorProvider, Connection[]> = {
			"google-drive": [],
			notion: [],
			onedrive: [],
		}
		for (const c of connections) {
			const p = c.provider as ConnectorProvider
			if (p in out) out[p].push(c)
		}
		return out
	}, [connections])

	const createPluginKeyMutation = 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) {
				if (res.status === 403) {
					throw new Error(
						"This plugin requires a Pro plan. Hermes and Codex are available on the Free plan.",
					)
				}
				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) => setConnectingPlugin(pluginId),
		onError: (err) => {
			toast.error("Failed to connect plugin", {
				description: err instanceof Error ? err.message : "Unknown error",
			})
		},
		onSettled: () => {
			setConnectingPlugin(null)
			queryClient.invalidateQueries({ queryKey: ["api-keys", org?.id] })
		},
		onSuccess: (data, pluginId) => {
			setNewKey({ open: true, key: data.key, pluginId })
		},
	})

	const addConnectionMutation = useMutation({
		mutationFn: async (provider: ConnectorProvider) => {
			const response = await $fetch("@post/connections/:provider", {
				params: { provider },
				body: {
					redirectUrl: window.location.href,
					containerTags: [],
				},
			})
			if ("data" in response && response.data && !("error" in response.data)) {
				return response.data
			}
			throw new Error(response.error?.message || "Failed to connect")
		},
		onMutate: (provider) => {
			setConnectingProvider(provider)
			analytics.connectionAuthStarted({ provider })
		},
		onError: (err) => {
			setConnectingProvider(null)
			toast.error("Failed to connect", {
				description: err instanceof Error ? err.message : "Unknown error",
			})
		},
		onSuccess: (data) => {
			if (data?.authLink) {
				window.location.href = data.authLink
				return
			}
			setConnectingProvider(null)
			toast.error("Connect link missing — try again.")
		},
	})

	const handleRevokePluginKey = async (keyId: string) => {
		try {
			await authClient.apiKey.delete({ keyId })
			toast.success("Plugin disconnected")
			refetchKeys()
		} catch {
			toast.error("Failed to disconnect plugin")
		}
	}

	const handleUpgrade = async () => {
		try {
			const result = await autumn.attach({
				planId: "api_pro",
				successUrl: `${window.location.origin}/?view=integrations`,
			})
			if (result?.paymentUrl) {
				window.open(result.paymentUrl, "_self")
				return
			}
			autumn.refetch?.()
		} catch (error) {
			console.error(error)
			toast.error("Failed to start checkout. Please try again.")
		}
	}

	const availablePluginIds = pluginsData?.plugins ?? Object.keys(PLUGIN_CATALOG)
	const enabledPluginIds = new Set(
		availablePluginIds.filter((id) => PLUGIN_CATALOG[id]),
	)

	const [category, setCategory] = useQueryState("cat", catParam)
	const [, setAddDoc] = useQueryState("add", addDocumentParam)
	const [mcpClient, setMcpClient] = useQueryState("mcpClient", parseAsString)
	const [mcpModalOpen, setMcpModalOpen] = useState(false)
	const [search, setSearch] = useState("")
	const [searchExpanded, setSearchExpanded] = useState(false)

	const openMcpClient = (key: MCPClientKey) => {
		void setMcpClient(key)
		setMcpModalOpen(true)
	}

	const closeMcpModal = () => {
		setMcpModalOpen(false)
		void setMcpClient(null)
	}

	const activeMcpClient = mcpClient
		? MCP_CLIENTS.find((c) => c.key === mcpClient)
		: undefined

	const allItems = useMemo<Item[]>(
		() =>
			SECTIONS.flatMap((s) => s.items(PLUGIN_CATALOG)).filter(
				(item) => item.kind !== "plugin" || enabledPluginIds.has(item.pluginId),
			),
		[enabledPluginIds],
	)

	const isItemConnected = useCallback(
		(item: Item): boolean => {
			if (item.kind === "plugin") {
				return activePluginById.has(item.pluginId)
			}
			if (item.kind === "connector") {
				return connectionsByProvider[item.provider].length > 0
			}
			return false
		},
		[activePluginById, connectionsByProvider],
	)

	const counts = useMemo<Record<CategoryFilter, number>>(
		() => ({
			all: allItems.length,
			connected: allItems.filter(isItemConnected).length,
			plugins: allItems.filter((i) => itemCategory(i) === "plugins").length,
			"knowledge-bases": allItems.filter(
				(i) => itemCategory(i) === "knowledge-bases",
			).length,
			"apps-extensions": allItems.filter(
				(i) => itemCategory(i) === "apps-extensions",
			).length,
			"ai-clients": allItems.filter((i) => itemCategory(i) === "ai-clients")
				.length,
		}),
		[allItems, isItemConnected],
	)

	useEffect(() => {
		if (category !== "all" && counts[category] === 0) {
			void setCategory("all")
		}
	}, [category, counts, setCategory])

	const claudeCodeConnected = activePluginById.has("claude_code")
	const claudeCodeNeedsPro =
		!isAutumnLoading && !hasProProduct && !isFreeTierPlugin("claude_code")

	const featuredPicks: FeaturedPick[] = [
		{
			id: "feat-mcp",
			name: "Supermemory MCP",
			headline: "Your AI tools forget everything between chats.",
			support: "one setup gives Cursor, Claude & ChatGPT your memory",
			tagline: "Plug your memory into any MCP client.",
			icon: <MCPIcon className="size-8" />,
			backdrop: (
				<Image
					src="/onboarding/mcp.png"
					alt=""
					width={380}
					height={380}
					className="object-contain"
				/>
			),
			docsUrl: "https://docs.supermemory.ai/supermemory-mcp/introduction",
			ctaLabel: "Connect",
			onCta: () => {
				void setMcpClient(null)
				setViewMode("mcp")
			},
		},
		{
			id: "feat-claude-code",
			name: "Claude Code plugin",
			headline: "Stop re-explaining your codebase every session.",
			support: "remembers your conventions, decisions & project context",
			tagline: "Long-term memory for your Claude Code sessions.",
			icon: (
				<Image
					src="/images/plugins/claude-code.svg"
					alt="Claude Code"
					width={32}
					height={32}
					className="rounded"
				/>
			),
			backdrop: (
				<Image
					src="/images/plugins/claude-code.svg"
					alt=""
					width={360}
					height={360}
					className="object-contain"
				/>
			),
			docsUrl: "https://docs.supermemory.ai/integrations/claude-code",
			ctaLabel: claudeCodeConnected
				? "Active"
				: claudeCodeNeedsPro
					? "Upgrade"
					: "Connect",
			onCta: () => {
				if (claudeCodeConnected) return
				if (claudeCodeNeedsPro) {
					handleUpgrade()
					return
				}
				createPluginKeyMutation.mutate("claude_code")
			},
		},
		{
			id: "feat-chrome",
			name: "Chrome Extension",
			headline: "That article you'll “read later”? Gone by next week.",
			support: "save anything on the web in one click",
			tagline: "Save anything on the web, straight from your browser.",
			icon: <ChromeIcon className="size-8" />,
			backdrop: <ChromeIcon className="size-96" />,
			ctaLabel: "Connect",
			onCta: () => {
				window.open(CHROME_EXTENSION_URL, "_blank", "noopener,noreferrer")
				analytics.onboardingChromeExtensionClicked({ source: "integrations" })
			},
		},
	]

	const q = search.trim().toLowerCase()
	const visibleItems = allItems.filter((item) => {
		if (category === "connected" && !isItemConnected(item)) return false
		if (
			category !== "all" &&
			category !== "connected" &&
			itemCategory(item) !== category
		)
			return false
		if (q) {
			const hay = `${item.name} ${item.tagline}`.toLowerCase()
			if (!hay.includes(q)) return false
		}
		return true
	})

	const trackCard = (item: Item) =>
		analytics.integrationCardClicked({
			kind: item.kind,
			id: item.id,
			name: item.name,
		})

	const renderRight = (item: Item): ReactNode => {
		switch (item.kind) {
			case "plugin": {
				const activeKey = activePluginById.get(item.pluginId)
				const activeCount = activeCountByPlugin.get(item.pluginId) ?? 0
				const needsProUpgrade =
					!isAutumnLoading && !hasProProduct && !isFreeTierPlugin(item.pluginId)
				if (activeKey) {
					const busy = connectingPlugin === item.pluginId
					return (
						<div className="flex w-full items-center justify-between gap-2">
							<ActiveButton
								count={activeCount}
								lastActive={activeKey.lastRequest}
								onClick={() => {
									trackCard(item)
									setConnectedPluginId(item.pluginId)
								}}
							/>
							<button
								type="button"
								aria-label={`Connect ${item.name} to another agent`}
								title="Connect another agent"
								onClick={() => {
									if (needsProUpgrade) {
										handleUpgrade()
										return
									}
									trackCard(item)
									createPluginKeyMutation.mutate(item.pluginId)
								}}
								disabled={!!connectingPlugin}
								className={cn(
									"flex size-8 shrink-0 items-center justify-center rounded-full bg-[#0D121A] text-[#A1A1AA] transition-colors hover:text-[#FAFAFA] disabled:opacity-50 sm:size-9",
									"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.7)]",
								)}
							>
								{busy ? (
									<Loader className="size-3.5 animate-spin" />
								) : (
									<Plus className="size-4" />
								)}
							</button>
						</div>
					)
				}
				if (setupPluginIds.has(item.pluginId)) {
					return (
						<FinishSetupButton
							onClick={() => {
								trackCard(item)
								setFinishSetupPluginId(item.pluginId)
							}}
						/>
					)
				}
				if (needsProUpgrade) {
					return (
						<PillButton onClick={handleUpgrade}>
							<Zap className="size-3.5 text-[#4BA0FA]" /> Upgrade
						</PillButton>
					)
				}
				const busy = connectingPlugin === item.pluginId
				return (
					<PillButton
						onClick={() => {
							trackCard(item)
							createPluginKeyMutation.mutate(item.pluginId)
						}}
						disabled={!!connectingPlugin}
					>
						{busy ? (
							<>
								<Loader className="size-3.5 animate-spin" /> Connecting…
							</>
						) : (
							"Connect"
						)}
					</PillButton>
				)
			}
			case "connector": {
				const count = connectionsByProvider[item.provider].length
				const needsProUpgrade = !isAutumnLoading && !hasProProduct
				if (count > 0) {
					return (
						<div className="flex w-full items-center justify-between gap-2">
							<ConnectionsCountPill count={count} />
							<button
								type="button"
								aria-label="Add another knowledge source"
								title="Add another knowledge source"
								onClick={() => {
									trackCard(item)
									void setAddDoc("connect")
								}}
								className={cn(
									"flex size-8 shrink-0 items-center justify-center rounded-full bg-[#0D121A] text-[#A1A1AA] transition-colors hover:text-[#FAFAFA] sm:size-9",
									"shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.7)]",
								)}
							>
								<Plus className="size-4" />
							</button>
						</div>
					)
				}
				if (needsProUpgrade) {
					return (
						<PillButton onClick={handleUpgrade}>
							<Zap className="size-3.5 text-[#4BA0FA]" /> Upgrade
						</PillButton>
					)
				}
				const busy = connectingProvider === item.provider
				return (
					<PillButton
						onClick={() => {
							trackCard(item)
							addConnectionMutation.mutate(item.provider)
						}}
						disabled={!!connectingProvider}
					>
						{busy ? (
							<>
								<Loader className="size-3.5 animate-spin" /> Connecting…
							</>
						) : (
							"Connect"
						)}
					</PillButton>
				)
			}
			case "client": {
				if (item.action.type === "external") {
					return (
						<PillButton
							onClick={() => {
								trackCard(item)
								window.open(
									(item.action as { type: "external"; href: string }).href,
									"_blank",
									"noopener,noreferrer",
								)
								analytics.onboardingChromeExtensionClicked({
									source: "integrations",
								})
							}}
						>
							Connect
						</PillButton>
					)
				}
				return (
					<PillButton
						onClick={() => {
							trackCard(item)
							setViewMode(
								(item.action as { type: "view"; viewMode: ViewParamValue })
									.viewMode,
							)
						}}
					>
						Connect
					</PillButton>
				)
			}
			case "mcp-client":
				return (
					<PillButton
						onClick={() => {
							trackCard(item)
							openMcpClient(item.clientKey)
						}}
					>
						Connect
					</PillButton>
				)
			case "import":
				return (
					<PillButton
						onClick={() => {
							trackCard(item)
							setViewMode(item.viewMode)
						}}
					>
						Connect
					</PillButton>
				)
		}
	}

	const renderItemCard = (item: Item) => (
		<ItemCard
			key={item.id}
			icon={item.icon}
			name={item.name}
			tagline={item.tagline}
			pro={item.pro}
			docsUrl={item.docsUrl}
			leftIndicator={renderLeftIndicator(item)}
			rightSlot={renderRight(item)}
		/>
	)

	const renderLeftIndicator = (_item: Item): ReactNode => {
		return null
	}

	const dialogPlugin = newKey.pluginId
		? PLUGIN_CATALOG[newKey.pluginId]
		: undefined
	const connectedDialogPlugin = connectedPluginId
		? PLUGIN_CATALOG[connectedPluginId]
		: undefined
	const connectedDialogKeys = connectedPluginId
		? activePlugins.filter((key) => key.pluginId === connectedPluginId)
		: []
	const connectedDialogNeedsPro =
		!!connectedPluginId &&
		!isAutumnLoading &&
		!hasProProduct &&
		!isFreeTierPlugin(connectedPluginId)
	const finishSetupPlugin = finishSetupPluginId
		? PLUGIN_CATALOG[finishSetupPluginId]
		: undefined
	const finishSetupSteps = finishSetupPlugin?.installSteps ?? []
	const pluginSteps = dialogPlugin?.installSteps ?? []
	const stepsEmbedKey = pluginSteps.some((s) => s.code?.includes("sm_..."))
	const skipGeneratedKeyStep = stepsEmbedKey || !!dialogPlugin?.usesOAuth
	const setupSteps: InstallStep[] = skipGeneratedKeyStep
		? pluginSteps
		: [
				{
					title: "Copy your API key",
					description:
						"You won't be able to see it again — store it somewhere safe.",
					code: newKey.key,
					copyLabel: "API key",
					secret: true,
				},
				...pluginSteps,
			]

	return (
		<div className="flex-1 p-4 md:p-6 pt-2">
			<div className="mx-auto flex max-w-5xl flex-col gap-5">
				{!q && <FeaturedHero picks={featuredPicks} />}

				<div
					className={cn(
						"relative overflow-hidden rounded-[14px] bg-[#191D24] p-4 sm:p-6",
						"shadow-[inset_2.42px_2.42px_4.263px_rgba(11,15,21,0.7)]",
					)}
				>
					<div className="mb-4 flex items-center justify-between gap-2">
						<div className="min-w-0 shrink">
							<CategoryFilterToggle
								value={category}
								onChange={(v) => void setCategory(v)}
								counts={counts}
								compact={searchExpanded || !!search}
							/>
						</div>
						<SearchToggle
							value={search}
							onChange={setSearch}
							expanded={searchExpanded}
							setExpanded={setSearchExpanded}
						/>
					</div>
					{visibleItems.length === 0 ? (
						<p
							className={cn(
								dmSans125ClassName(),
								"py-6 text-center text-[13px] text-[#A1A1AA]",
							)}
						>
							{q
								? `No integrations match “${search}”.`
								: "Nothing in this category yet."}
						</p>
					) : q || category !== "all" ? (
						<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
							{visibleItems.map((item) => renderItemCard(item))}
						</div>
					) : (
						<div className="flex flex-col gap-6">
							{SECTION_ORDER.map((cat) => {
								const items = visibleItems.filter(
									(i) => itemCategory(i) === cat,
								)
								if (items.length === 0) return null
								return (
									<SectionRail key={cat} label={CATEGORY_LABEL[cat]}>
										{items.map((item) => (
											<div key={item.id} className="w-[280px] shrink-0">
												{renderItemCard(item)}
											</div>
										))}
									</SectionRail>
								)
							})}
						</div>
					)}
				</div>
			</div>

			<Dialog
				open={newKey.open}
				onOpenChange={(open) =>
					setNewKey((s) => ({
						open,
						key: open ? s.key : "",
						pluginId: open ? s.pluginId : null,
					}))
				}
			>
				<DialogContent
					showCloseButton={false}
					style={{
						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",
					}}
					className={cn(
						dmSans125ClassName(),
						"flex max-h-[88dvh] flex-col gap-3 overflow-hidden border border-white/[0.12] bg-[#1B1F24] p-0 px-3 pt-3 pb-4 rounded-2xl md:px-4 sm:max-w-[560px] sm:rounded-[22px]",
					)}
				>
					<DialogTitle className="sr-only">
						Set up {dialogPlugin?.name ?? "your plugin"}
					</DialogTitle>
					<div className="flex shrink-0 items-center gap-3">
						{dialogPlugin && (
							<IconBox>
								<Image
									src={dialogPlugin.icon}
									alt={dialogPlugin.name}
									width={24}
									height={24}
								/>
							</IconBox>
						)}
						<div className="min-w-0 flex-1">
							<p className="truncate text-[16px] font-semibold leading-tight text-[#FAFAFA]">
								Set up {dialogPlugin?.name ?? "your plugin"}
							</p>
							<p className="mt-0.5 truncate text-[12px] text-[#A1A1AA]">
								Copy your key and run these steps to finish.
							</p>
						</div>
						<div className="flex shrink-0 items-center gap-2">
							{dialogPlugin?.docsUrl && (
								<a
									href={dialogPlugin.docsUrl}
									target="_blank"
									rel="noopener noreferrer"
									className={cn(
										dmSans125ClassName(),
										"flex h-7 items-center gap-1.5 rounded-full bg-[#0D121A] px-3 text-[12px] text-[#A1A1AA] transition-colors hover:text-white",
										INSET,
									)}
								>
									<BookOpen className="size-3.5" /> Docs
								</a>
							)}
							<DialogPrimitive.Close
								type="button"
								aria-label="Close"
								className={cn(
									"flex size-7 items-center justify-center rounded-full bg-[#0D121A] transition-opacity hover:opacity-80 focus:outline-none",
									INSET,
								)}
							>
								<X className="size-4 text-[#737373]" />
							</DialogPrimitive.Close>
						</div>
					</div>
					<div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
						<div
							className={cn(
								"min-w-0 rounded-[14px] bg-[#14161A] p-4 sm:p-5",
								INSET,
							)}
						>
							<InstallSteps steps={setupSteps} apiKey={newKey.key} />
						</div>
					</div>
					<div className="flex shrink-0 items-center justify-end">
						<button
							type="button"
							onClick={() =>
								setNewKey({ open: false, key: "", pluginId: null })
							}
							className={cn(
								dmSans125ClassName(),
								"flex h-9 items-center gap-1.5 rounded-full bg-[#0D121A] px-5 text-[13px] font-medium text-[#FAFAFA] transition-opacity hover:opacity-80",
								INSET,
							)}
						>
							<Check className="size-3.5 text-[#4BA0FA]" /> Done
						</button>
					</div>
				</DialogContent>
			</Dialog>

			<Dialog
				open={!!connectedPluginId}
				onOpenChange={(open) => {
					if (!open) setConnectedPluginId(null)
				}}
			>
				<DialogContent
					showCloseButton={false}
					style={{
						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",
					}}
					className={cn(
						dmSans125ClassName(),
						"flex max-h-[88dvh] flex-col gap-3 overflow-hidden border border-white/[0.12] bg-[#1B1F24] p-0 px-3 pt-3 pb-4 rounded-2xl md:px-4 sm:max-w-[520px] sm:rounded-[22px]",
					)}
				>
					<DialogTitle className="sr-only">
						{connectedDialogPlugin?.name ?? "Plugin"} connection
					</DialogTitle>
					<div className="flex shrink-0 items-center gap-3">
						{connectedDialogPlugin && (
							<IconBox>
								<Image
									src={connectedDialogPlugin.icon}
									alt={connectedDialogPlugin.name}
									width={24}
									height={24}
								/>
							</IconBox>
						)}
						<div className="min-w-0 flex-1">
							<p className="truncate text-[16px] font-semibold leading-tight text-[#FAFAFA]">
								{connectedDialogPlugin?.name ?? "Plugin"}
							</p>
							<p className="mt-0.5 flex items-center gap-1.5 text-[12px] text-[#00AC3F]">
								<span className="size-[7px] rounded-full bg-[#00AC3F]" />
								Active
								{activePluginById.get(connectedPluginId ?? "")?.lastRequest && (
									<span className="text-[#737373]">
										·{" "}
										{formatRelativeTime(
											activePluginById.get(connectedPluginId ?? "")
												?.lastRequest,
										)}
									</span>
								)}
							</p>
						</div>
						<div className="flex shrink-0 items-center gap-2">
							{connectedDialogPlugin?.docsUrl && (
								<a
									href={connectedDialogPlugin.docsUrl}
									target="_blank"
									rel="noopener noreferrer"
									className={cn(
										dmSans125ClassName(),
										"flex h-7 items-center gap-1.5 rounded-full bg-[#0D121A] px-3 text-[12px] text-[#A1A1AA] transition-colors hover:text-white",
										INSET,
									)}
								>
									<BookOpen className="size-3.5" /> Docs
								</a>
							)}
							<DialogPrimitive.Close
								type="button"
								aria-label="Close"
								className={cn(
									"flex size-7 items-center justify-center rounded-full bg-[#0D121A] transition-opacity hover:opacity-80 focus:outline-none",
									INSET,
								)}
							>
								<X className="size-4 text-[#737373]" />
							</DialogPrimitive.Close>
						</div>
					</div>
					<div
						className={cn(
							"flex min-w-0 flex-col gap-2 rounded-[14px] bg-[#14161A] p-4",
							INSET,
						)}
					>
						<p className="text-[11px] font-medium uppercase tracking-wide text-[#737373]">
							{connectedDialogKeys.length > 1
								? `${connectedDialogKeys.length} connections`
								: "Connection"}
						</p>
						{connectedDialogKeys.length > 0 ? (
							<div className="flex flex-col gap-1.5">
								{connectedDialogKeys.map((key) => (
									<div
										key={key.keyId}
										className="flex min-w-0 items-center justify-between gap-3 rounded-lg bg-[#0D121A]/70 px-3 py-2"
									>
										<span className="min-w-0 flex-1 truncate font-mono text-[12px] text-[#A1A1AA]">
											{key.keyStart ? `${key.keyStart}...` : "API key"}
										</span>
										<DisconnectButton
											onConfirm={() => void handleRevokePluginKey(key.keyId)}
										/>
									</div>
								))}
							</div>
						) : (
							<p className="text-[12px] text-[#A1A1AA]">
								No active connection was found.
							</p>
						)}
						<p className="mt-1 text-[12px] text-[#737373]">
							Connect this plugin to another agent to run them in parallel.
						</p>
					</div>
					<div className="flex shrink-0 items-center justify-between gap-2">
						{connectedDialogNeedsPro ? (
							<PillButton onClick={handleUpgrade}>
								<Zap className="size-3.5 text-[#4BA0FA]" /> Upgrade to connect
								more
							</PillButton>
						) : (
							<PillButton
								onClick={() => {
									if (!connectedPluginId) return
									const pluginId = connectedPluginId
									setConnectedPluginId(null)
									createPluginKeyMutation.mutate(pluginId)
								}}
								disabled={!!connectingPlugin}
							>
								{connectingPlugin === connectedPluginId ? (
									<>
										<Loader className="size-3.5 animate-spin" /> Connecting…
									</>
								) : (
									<>
										<Plus className="size-3.5 text-[#4BA0FA]" /> Connect another
									</>
								)}
							</PillButton>
						)}
						<DialogPrimitive.Close asChild>
							<button
								type="button"
								className={cn(
									dmSans125ClassName(),
									"flex h-9 items-center gap-1.5 rounded-full bg-[#0D121A] px-5 text-[13px] font-medium text-[#FAFAFA] transition-opacity hover:opacity-80",
									INSET,
								)}
							>
								<Check className="size-3.5 text-[#4BA0FA]" /> Done
							</button>
						</DialogPrimitive.Close>
					</div>
				</DialogContent>
			</Dialog>

			<Dialog
				open={!!finishSetupPluginId}
				onOpenChange={(open) => {
					if (!open) setFinishSetupPluginId(null)
				}}
			>
				<DialogContent
					showCloseButton={false}
					style={{
						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",
					}}
					className={cn(
						dmSans125ClassName(),
						"flex max-h-[88dvh] flex-col gap-3 overflow-hidden border border-white/[0.12] bg-[#1B1F24] p-0 px-3 pt-3 pb-4 rounded-2xl md:px-4 sm:max-w-[560px] sm:rounded-[22px]",
					)}
				>
					<DialogTitle className="sr-only">
						Finish setup {finishSetupPlugin?.name ?? "plugin"}
					</DialogTitle>
					<div className="flex shrink-0 items-center gap-3">
						{finishSetupPlugin && (
							<IconBox>
								<Image
									src={finishSetupPlugin.icon}
									alt={finishSetupPlugin.name}
									width={24}
									height={24}
								/>
							</IconBox>
						)}
						<div className="min-w-0 flex-1">
							<p className="truncate text-[16px] font-semibold leading-tight text-[#FAFAFA]">
								Finish setup {finishSetupPlugin?.name ?? "plugin"}
							</p>
							<p className="mt-0.5 truncate text-[12px] text-[#A1A1AA]">
								Complete install in the tool — this card turns active after the
								first API call.
							</p>
						</div>
						<DialogPrimitive.Close
							type="button"
							aria-label="Close"
							className={cn(
								"flex size-7 items-center justify-center rounded-full bg-[#0D121A] transition-opacity hover:opacity-80 focus:outline-none",
								INSET,
							)}
						>
							<X className="size-4 text-[#737373]" />
						</DialogPrimitive.Close>
					</div>
					<div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
						<div
							className={cn(
								"min-w-0 rounded-[14px] bg-[#14161A] p-4 sm:p-5",
								INSET,
							)}
						>
							{finishSetupSteps.length > 0 ? (
								<InstallSteps steps={finishSetupSteps} />
							) : (
								<p className="text-[13px] text-[#A1A1AA]">
									Open {finishSetupPlugin?.name ?? "the plugin"} and finish
									authentication, then send a test memory.
								</p>
							)}
						</div>
					</div>
					<div className="flex shrink-0 items-center justify-end">
						<DialogPrimitive.Close asChild>
							<button
								type="button"
								className={cn(
									dmSans125ClassName(),
									"flex h-9 items-center gap-1.5 rounded-full bg-[#0D121A] px-5 text-[13px] font-medium text-[#FAFAFA] transition-opacity hover:opacity-80",
									INSET,
								)}
							>
								<Check className="size-3.5 text-[#4BA0FA]" /> Done
							</button>
						</DialogPrimitive.Close>
					</div>
				</DialogContent>
			</Dialog>

			<Dialog
				open={mcpModalOpen}
				onOpenChange={(open) => {
					if (!open) closeMcpModal()
					else setMcpModalOpen(true)
				}}
			>
				<DialogContent
					showCloseButton={false}
					style={{
						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",
					}}
					className={cn(
						dmSans125ClassName(),
						"flex max-h-[88dvh] flex-col gap-3 overflow-hidden border border-white/[0.12] bg-[#1B1F24] p-0 px-3 pt-3 pb-4 rounded-2xl md:px-4 sm:max-w-[640px] sm:rounded-[22px]",
					)}
				>
					<DialogTitle className="sr-only">
						Set up {activeMcpClient?.name ?? "MCP client"}
					</DialogTitle>
					<div className="flex shrink-0 items-center gap-3">
						<IconBox>
							{activeMcpClient && activeMcpClient.key !== "mcp-url" ? (
								<Image
									src={mcpClientIconSrc(activeMcpClient.key)}
									alt={activeMcpClient.name}
									width={24}
									height={24}
									unoptimized
									className="size-6 rounded object-contain"
								/>
							) : (
								<MCPIcon className="size-6" />
							)}
						</IconBox>
						<div className="min-w-0 flex-1">
							<p className="truncate text-[16px] font-semibold leading-tight text-[#FAFAFA]">
								Set up {activeMcpClient?.name ?? "MCP client"}
							</p>
							<p className="mt-0.5 truncate text-[12px] text-[#A1A1AA]">
								Connect supermemory MCP to{" "}
								{activeMcpClient?.name ?? "your client"}.
							</p>
						</div>
						<div className="flex shrink-0 items-center gap-2">
							<a
								href="https://docs.supermemory.ai/supermemory-mcp/introduction"
								target="_blank"
								rel="noopener noreferrer"
								className={cn(
									dmSans125ClassName(),
									"flex h-7 items-center gap-1.5 rounded-full bg-[#0D121A] px-3 text-[12px] text-[#A1A1AA] transition-colors hover:text-white",
									INSET,
								)}
							>
								<BookOpen className="size-3.5" /> Docs
							</a>
							<DialogPrimitive.Close
								type="button"
								aria-label="Close"
								className={cn(
									"flex size-7 items-center justify-center rounded-full bg-[#0D121A] transition-opacity hover:opacity-80 focus:outline-none",
									INSET,
								)}
							>
								<X className="size-4 text-[#737373]" />
							</DialogPrimitive.Close>
						</div>
					</div>
					<div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
						<div
							className={cn(
								"min-w-0 rounded-[14px] bg-[#14161A] p-4 sm:p-5",
								INSET,
							)}
						>
							<MCPSteps variant="embedded" />
						</div>
					</div>
					<div className="flex shrink-0 items-center justify-end">
						<button
							type="button"
							onClick={closeMcpModal}
							className={cn(
								dmSans125ClassName(),
								"flex h-9 items-center gap-1.5 rounded-full bg-[#0D121A] px-5 text-[13px] font-medium text-[#FAFAFA] transition-opacity hover:opacity-80",
								INSET,
							)}
						>
							<Check className="size-3.5 text-[#4BA0FA]" /> Done
						</button>
					</div>
				</DialogContent>
			</Dialog>
		</div>
	)
}
