"use client"

import { useState, useEffect, useCallback, useRef } from "react"
import { motion, AnimatePresence } from "motion/react"
import { useAgent } from "agents/react"
import { useAgentChat } from "@cloudflare/ai-chat/react"
import NovaOrb from "@/components/nova/nova-orb"
import { Button } from "@ui/components/button"
import {
	PanelRightCloseIcon,
	SendIcon,
	CheckIcon,
	XIcon,
	Loader2,
} from "lucide-react"
import { collectValidUrls } from "@/lib/url-helpers"
import { $fetch } from "@lib/api"
import { cn } from "@lib/utils"
import { dmSansClassName } from "@/lib/fonts"
import { useAuth } from "@lib/auth-context"
import { useProject } from "@/stores"
import { Streamdown } from "streamdown"
import { useIsMobile } from "@hooks/use-mobile"

interface ChatSidebarProps {
	formData: {
		twitter: string
		linkedin: string
		description: string
		otherLinks: string[]
	} | null
}

interface DraftDoc {
	kind: "likes" | "link" | "x_research"
	content: string
	metadata: Record<string, string>
	title?: string
	url?: string
}

export function ChatSidebar({ formData }: ChatSidebarProps) {
	const { user } = useAuth()
	const { selectedProject } = useProject()
	const isMobile = useIsMobile()
	const [message, setMessage] = useState("")
	const [isChatOpen, setIsChatOpen] = useState(!isMobile)
	const [timelineMessages, setTimelineMessages] = useState<
		{
			message: string
			type?: "formData" | "exa" | "memory" | "waiting"
			memories?: {
				url: string
				title: string
				description: string
				fullContent: string
			}[]
			url?: string
			title?: string
			description?: string
		}[]
	>([])
	const [isLoading, setIsLoading] = useState(false)
	const [isFetchingDrafts, setIsFetchingDrafts] = useState(false)
	const [draftDocs, setDraftDocs] = useState<DraftDoc[]>([])
	const [xResearchStatus, setXResearchStatus] = useState<
		"correct" | "incorrect" | null
	>(null)
	const [isConfirmed, setIsConfirmed] = useState(false)
	const [processingByUrl, setProcessingByUrl] = useState<
		Record<string, boolean>
	>({})
	const displayedMemoriesRef = useRef<Set<string>>(new Set())
	const contextInjectedRef = useRef(false)
	const draftsBuiltRef = useRef(false)
	const isProcessingRef = useRef(false)
	const draftRequestIdRef = useRef(0)

	const backendUrl = new URL(process.env.NEXT_PUBLIC_BACKEND_URL!)
	const agent = useAgent({
		agent: "chat-agent",
		name: user?.id ?? "anonymous",
		host: backendUrl.host,
	})

	useEffect(() => {
		agent.setState({
			model: "gemini-2.5-pro" as const,
			projectId: selectedProject,
		})
	}, [agent, selectedProject])

	const {
		messages: chatMessages,
		sendMessage,
		status,
	} = useAgentChat({
		agent,
		getInitialMessages: null,
		credentials: "include",
	})

	const buildOnboardingContext = useCallback(() => {
		if (!formData) return ""

		const contextParts: string[] = []

		if (formData.description?.trim()) {
			contextParts.push(`User's interests/likes: ${formData.description}`)
		}

		if (formData.twitter) {
			contextParts.push(`X/Twitter profile: ${formData.twitter}`)
		}

		if (formData.linkedin) {
			contextParts.push(`LinkedIn profile: ${formData.linkedin}`)
		}

		if (formData.otherLinks.length > 0) {
			contextParts.push(`Other links: ${formData.otherLinks.join(", ")}`)
		}

		const memoryTexts = timelineMessages
			.filter((msg) => msg.type === "memory" && msg.memories)
			.flatMap(
				(msg) => msg.memories?.map((m) => `${m.title}: ${m.description}`) || [],
			)

		if (memoryTexts.length > 0) {
			contextParts.push(`Extracted memories:\n${memoryTexts.join("\n")}`)
		}

		return contextParts.join("\n\n")
	}, [formData, timelineMessages])

	const handleSend = () => {
		if (!message.trim() || status === "submitted" || status === "streaming")
			return

		let messageToSend = message

		const context = buildOnboardingContext()

		if (context && !contextInjectedRef.current && chatMessages.length === 0) {
			messageToSend = `${context}\n\nUser question: ${message}`
			contextInjectedRef.current = true
		}

		sendMessage({ text: messageToSend })
		setMessage("")
	}

	const handleKeyDown = (e: React.KeyboardEvent) => {
		if (e.key === "Enter" && !e.shiftKey) {
			e.preventDefault()
			handleSend()
		}
	}

	const toggleChat = () => {
		setIsChatOpen(!isChatOpen)
	}

	const pollForMemories = useCallback(
		async (documentIds: string[]) => {
			const maxAttempts = 30 // 30 attempts * 3 seconds = 90 seconds max
			const pollInterval = 3000 // 3 seconds

			for (let attempt = 0; attempt < maxAttempts; attempt++) {
				try {
					const response = await $fetch("@get/documents/:id", {
						params: { id: documentIds[0] ?? "" },
						disableValidation: true,
					})

					console.log("response", response)

					if (response.data) {
						const document = response.data

						if (document.memories && document.memories.length > 0) {
							const newMemories: {
								url: string
								title: string
								description: string
								fullContent: string
							}[] = []

							document.memories.forEach(
								(memory: { memory: string; title?: string }) => {
									if (!displayedMemoriesRef.current.has(memory.memory)) {
										displayedMemoriesRef.current.add(memory.memory)
										newMemories.push({
											url: document.url || "",
											title: memory.title || document.title || "Memory",
											description: memory.memory || "",
											fullContent: memory.memory || "",
										})
									}
								},
							)

							if (newMemories.length > 0 && timelineMessages.length < 10) {
								setTimelineMessages((prev) => [
									...prev,
									{
										message: newMemories
											.map((memory) => memory.description)
											.join("\n"),
										type: "memory" as const,
										memories: newMemories,
									},
								])
							}
						}

						if (document.memories && document.memories.length > 0) {
							break
						}
					}

					await new Promise((resolve) => setTimeout(resolve, pollInterval))
				} catch (error) {
					console.warn("Error polling for memories:", error)
					await new Promise((resolve) => setTimeout(resolve, pollInterval))
				}
			}
		},
		[timelineMessages.length],
	)

	const buildDraftDocs = useCallback(async () => {
		if (!formData || draftsBuiltRef.current) return
		draftsBuiltRef.current = true

		const hasContent =
			formData.twitter ||
			formData.linkedin ||
			formData.otherLinks.length > 0 ||
			formData.description?.trim()

		if (!hasContent) return

		const requestId = ++draftRequestIdRef.current

		setIsFetchingDrafts(true)
		const drafts: DraftDoc[] = []

		const urls = collectValidUrls(formData.linkedin, formData.otherLinks)
		const allProcessingUrls: string[] = [...urls]
		if (formData.twitter) {
			allProcessingUrls.push(formData.twitter)
		}

		if (allProcessingUrls.length > 0) {
			setProcessingByUrl((prev) => {
				const next = { ...prev }
				for (const url of allProcessingUrls) {
					next[url] = true
				}
				return next
			})
		}

		try {
			if (formData.description?.trim()) {
				drafts.push({
					kind: "likes",
					content: formData.description,
					metadata: {
						sm_source: "consumer",
						description_source: "user_input",
					},
					title: "Your Interests",
				})
			}

			// Fetch each URL separately for per-link loading state
			const linkPromises = urls.map(async (url) => {
				try {
					const response = await fetch("/api/onboarding/extract-content", {
						method: "POST",
						headers: { "Content-Type": "application/json" },
						body: JSON.stringify({ urls: [url] }),
					})
					const data = await response.json()
					return data.results?.[0] || null
				} catch {
					return null
				} finally {
					// Clear this URL's processing state
					if (draftRequestIdRef.current === requestId) {
						setProcessingByUrl((prev) => ({ ...prev, [url]: false }))
					}
				}
			})

			// Fetch X/Twitter research
			const xResearchPromise = formData.twitter
				? (async () => {
						try {
							const response = await fetch("/api/onboarding/research", {
								method: "POST",
								headers: { "Content-Type": "application/json" },
								body: JSON.stringify({
									xUrl: formData.twitter,
									name: user?.name,
									email: user?.email,
								}),
							})
							if (!response.ok) return null
							const data = await response.json()
							return data?.text?.trim() || null
						} catch {
							return null
						} finally {
							// Clear twitter URL's processing state
							if (draftRequestIdRef.current === requestId) {
								setProcessingByUrl((prev) => ({
									...prev,
									[formData.twitter]: false,
								}))
							}
						}
					})()
				: Promise.resolve(null)

			const [exaResults, xResearchResult] = await Promise.all([
				Promise.all(linkPromises),
				xResearchPromise,
			])

			// Guard against stale request completing after a newer one
			if (draftRequestIdRef.current !== requestId) return

			for (const result of exaResults) {
				if (result && (result.text || result.description)) {
					drafts.push({
						kind: "link",
						content: result.text || result.description || "",
						metadata: {
							sm_source: "consumer",
							exa_url: result.url,
							exa_title: result.title,
						},
						title: result.title || "Extracted Content",
						url: result.url,
					})
				}
			}

			if (xResearchResult) {
				drafts.push({
					kind: "x_research",
					content: xResearchResult,
					metadata: {
						sm_source: "consumer",
						onboarding_source: "x_research",
						x_url: formData.twitter,
					},
					title: "X/Twitter Profile Research",
					url: formData.twitter,
				})
			}

			setDraftDocs(drafts)
		} catch (error) {
			console.warn("Error building draft docs:", error)
		} finally {
			if (draftRequestIdRef.current === requestId) {
				setIsFetchingDrafts(false)
			}
		}
	}, [formData, user])

	const handleConfirmDocs = useCallback(async () => {
		if (isConfirmed || isProcessingRef.current) return
		isProcessingRef.current = true
		setIsConfirmed(true)
		setIsLoading(true)

		try {
			const promises = draftDocs.map(async (draft) => {
				if (draft.kind === "x_research" && xResearchStatus !== "correct") {
					return null
				}

				try {
					const docResponse = await $fetch("@post/documents", {
						body: {
							content: draft.content,
							containerTags: ["sm_project_default"],
							metadata: draft.metadata,
						},
					})

					return docResponse.data?.id
				} catch (error) {
					console.warn("Error creating document:", error)
					return null
				}
			})

			const results = await Promise.all(promises)
			const documentIds = results.filter(
				(id): id is string => id !== null && id !== undefined,
			)

			if (documentIds.length > 0) {
				await pollForMemories(documentIds)
			}
		} catch (error) {
			console.warn("Error confirming documents:", error)
			setIsConfirmed(false)
		} finally {
			setIsLoading(false)
			isProcessingRef.current = false
		}
	}, [draftDocs, xResearchStatus, isConfirmed, pollForMemories])

	useEffect(() => {
		if (!formData) return

		const formDataMessages: typeof timelineMessages = []

		if (formData.twitter) {
			formDataMessages.push({
				message: formData.twitter,
				url: formData.twitter,
				title: "X/Twitter",
				description: formData.twitter,
				type: "formData" as const,
			})
		}

		if (formData.linkedin) {
			formDataMessages.push({
				message: formData.linkedin,
				url: formData.linkedin,
				title: "LinkedIn",
				description: formData.linkedin,
				type: "formData" as const,
			})
		}

		if (formData.otherLinks.length > 0) {
			formData.otherLinks.forEach((link) => {
				formDataMessages.push({
					message: link,
					url: link,
					title: "Link",
					description: link,
					type: "formData" as const,
				})
			})
		}

		if (formData.description?.trim()) {
			formDataMessages.push({
				message: formData.description,
				title: "Likes",
				description: formData.description,
				type: "formData" as const,
			})
		}

		setTimelineMessages(formDataMessages)
		buildDraftDocs()
	}, [formData, buildDraftDocs])

	return (
		<AnimatePresence mode="wait">
			{!isChatOpen ? (
				<motion.div
					key="closed"
					className={cn(
						"flex items-start justify-start",
						isMobile
							? "fixed bottom-4 right-4 z-50"
							: "absolute top-0 right-0 m-4",
						dmSansClassName(),
					)}
					layoutId="chat-toggle-button"
				>
					<motion.button
						onClick={toggleChat}
						className={cn(
							"flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-medium border border-[#17181A] text-white cursor-pointer shadow-lg",
							isMobile && "px-4 py-2",
						)}
						style={{
							background: "linear-gradient(180deg, #0A0E14 0%, #05070A 100%)",
						}}
					>
						<NovaOrb size={24} className="blur-none! z-10" />
						{!isMobile && "Chat with Nova"}
					</motion.button>
				</motion.div>
			) : (
				<motion.div
					key="open"
					className={cn(
						"bg-[#0A0E14] backdrop-blur-md flex flex-col",
						isMobile
							? "fixed inset-0 z-50 w-full h-dvh rounded-none m-0"
							: "w-[450px] h-[calc(100vh-110px)] rounded-2xl m-4",
						dmSansClassName(),
					)}
					initial={
						isMobile ? { y: "100%", opacity: 0 } : { x: "100px", opacity: 0 }
					}
					animate={{ x: 0, y: 0, opacity: 1 }}
					exit={
						isMobile ? { y: "100%", opacity: 0 } : { x: "100px", opacity: 0 }
					}
					transition={{ duration: 0.3, ease: "easeOut", bounce: 0 }}
				>
					<motion.button
						onClick={toggleChat}
						className={cn(
							"absolute top-4 right-4 flex items-center gap-2 rounded-full p-2 text-xs text-white cursor-pointer",
							isMobile && "bg-[#0D121A] border border-[#73737333]",
						)}
						style={
							isMobile
								? {
										boxShadow: "1.5px 1.5px 4.5px 0 rgba(0, 0, 0, 0.70) inset",
									}
								: {
										background:
											"linear-gradient(180deg, #0A0E14 0%, #05070A 100%)",
									}
						}
						layoutId="chat-toggle-button"
					>
						{isMobile ? (
							<XIcon className="size-4" />
						) : (
							<>
								<PanelRightCloseIcon className="size-4" />
								Close chat
							</>
						)}
					</motion.button>
					<div className="flex-1 flex flex-col px-4 space-y-3 pb-4 justify-end overflow-y-auto scrollbar-thin">
						{timelineMessages.map((msg, i) => (
							<div
								key={`message-${i}-${msg.message}`}
								className="flex items-start gap-2"
							>
								{msg.type === "waiting" ? (
									<div className="flex items-center gap-2 text-white/50">
										<NovaOrb size={30} className="blur-none!" />
										<span className="text-sm">{msg.message}</span>
									</div>
								) : (
									<>
										<div
											className={cn(
												"flex flex-col items-center justify-center w-[30px] h-full",
												i !== 0 && "",
											)}
										>
											{i === 0 && (
												<div className="w-3 h-3 bg-[#293952]/40 rounded-full mb-1" />
											)}
											<div className="w-px flex-1 bg-[#293952]/40" />
										</div>
										{msg.type === "formData" && (
											<div className="bg-[#293952]/40 rounded-lg p-2 px-3 space-y-1 flex-1">
												{msg.title && (
													<div className="flex items-center gap-2">
														<h3
															className="text-sm font-medium"
															style={{
																background:
																	"linear-gradient(90deg, #369BFD 0%, #36FDFD 30%, #36FDB5 100%)",
																WebkitBackgroundClip: "text",
																WebkitTextFillColor: "transparent",
																backgroundClip: "text",
															}}
														>
															{msg.title}
														</h3>
														{msg.url && processingByUrl[msg.url] && (
															<Loader2 className="h-3 w-3 animate-spin text-blue-400" />
														)}
													</div>
												)}
												{msg.url && (
													<a
														href={msg.url}
														target="_blank"
														rel="noopener noreferrer"
														className="text-xs text-blue-400 hover:underline break-all block"
													>
														{msg.url}
													</a>
												)}
												{msg.title === "Likes" && msg.description && (
													<p className="text-xs text-white/70 mt-1">
														{msg.description}
													</p>
												)}
											</div>
										)}
										{msg.type === "memory" && (
											<div className="space-y-2 w-full max-h-60 overflow-y-auto scrollbar-thin">
												{msg.memories?.map((memory) => (
													<div
														key={memory.url + memory.title}
														className="bg-[#293952]/40 rounded-lg p-2 px-3 space-y-2"
													>
														{memory.title && (
															<h3
																className="text-sm font-medium"
																style={{
																	background:
																		"linear-gradient(90deg, #369BFD 0%, #36FDFD 30%, #36FDB5 100%)",
																	WebkitBackgroundClip: "text",
																	WebkitTextFillColor: "transparent",
																	backgroundClip: "text",
																}}
															>
																{memory.title}
															</h3>
														)}
														{memory.url && (
															<a
																href={memory.url}
																target="_blank"
																rel="noopener noreferrer"
																className="text-xs text-blue-400 hover:underline break-all"
															>
																{memory.url}
															</a>
														)}
														{memory.description && (
															<p className="text-xs text-white/50 mt-1">
																{memory.description}
															</p>
														)}
													</div>
												))}
											</div>
										)}
									</>
								)}
							</div>
						))}
						{chatMessages.map((msg) => {
							if (msg.role === "user") {
								const text = msg.parts
									.filter((part) => part.type === "text")
									.map((part) => part.text)
									.join(" ")
								return (
									<div
										key={msg.id}
										className="flex items-start gap-2 justify-end"
									>
										<div className="bg-[#1B1F24] rounded-[12px] p-3 px-[14px] max-w-[80%]">
											<p className="text-sm text-white">{text}</p>
										</div>
									</div>
								)
							}
							if (msg.role === "assistant") {
								return (
									<div key={msg.id} className="flex items-start gap-2">
										<NovaOrb size={30} className="blur-none!" />
										<div className="flex-1">
											{msg.parts.map((part, partIndex) => {
												if (part.type === "text") {
													return (
														<div
															key={`${msg.id}-${partIndex}`}
															className="text-sm text-white/90 chat-markdown-content"
														>
															<Streamdown>{part.text}</Streamdown>
														</div>
													)
												}
												if (part.type === "tool-searchMemories") {
													if (
														part.state === "input-available" ||
														part.state === "input-streaming"
													) {
														return (
															<div
																key={`${msg.id}-${partIndex}`}
																className="text-xs text-white/50 italic"
															>
																Searching memories...
															</div>
														)
													}
												}
												return null
											})}
										</div>
									</div>
								)
							}
							return null
						})}
						{(status === "submitted" || status === "streaming") &&
							chatMessages[chatMessages.length - 1]?.role === "user" && (
								<div className="flex items-start gap-2">
									<NovaOrb size={30} className="blur-none!" />
									<span className="text-sm text-white/50">Thinking...</span>
								</div>
							)}
						{timelineMessages.length === 0 &&
							chatMessages.length === 0 &&
							!isLoading &&
							!formData && (
								<div className="flex items-center gap-2 text-white/50">
									<NovaOrb size={28} className="blur-none!" />
									<span className="text-sm">Waiting for your input</span>
								</div>
							)}
						{isLoading && (
							<div className="flex items-center gap-2 text-foreground/50">
								<NovaOrb size={28} className="blur-none!" />
								<span className="text-sm">Extracting memories...</span>
							</div>
						)}
					</div>

					{draftDocs.some((d) => d.kind === "x_research") && !isConfirmed && (
						<div className="px-4 pb-2 space-y-3">
							<div className="bg-[#293952]/40 rounded-lg p-3 space-y-2">
								<h3
									className="text-sm font-medium"
									style={{
										background:
											"linear-gradient(90deg, #369BFD 0%, #36FDFD 30%, #36FDB5 100%)",
										WebkitBackgroundClip: "text",
										WebkitTextFillColor: "transparent",
										backgroundClip: "text",
									}}
								>
									Your Profile Summary
								</h3>
								<div className="overflow-y-auto scrollbar-thin max-h-32">
									<p className="text-xs text-white/70">
										{draftDocs.find((d) => d.kind === "x_research")?.content}
									</p>
								</div>
								<div className="flex items-center gap-2 pt-2">
									<span className="text-xs text-white/50">
										Is this accurate?
									</span>
									<button
										type="button"
										onClick={() => {
											setXResearchStatus("correct")
											handleConfirmDocs()
										}}
										disabled={isConfirmed || isLoading}
										className={cn(
											"flex items-center gap-1 px-2 py-1 rounded-md text-xs transition-colors cursor-pointer",
											xResearchStatus === "correct"
												? "bg-green-500/20 text-green-400 border border-green-500/40"
												: "bg-[#1B1F24] text-white/50 hover:text-white/70",
											(isConfirmed || isLoading) &&
												"opacity-50 cursor-not-allowed",
										)}
									>
										<CheckIcon className="size-3" />
										Correct
									</button>
									<button
										type="button"
										onClick={() => setXResearchStatus("incorrect")}
										className={cn(
											"flex items-center gap-1 px-2 py-1 rounded-md text-xs transition-colors cursor-pointer",
											xResearchStatus === "incorrect"
												? "bg-red-500/20 text-red-400 border border-red-500/40"
												: "bg-[#1B1F24] text-white/50 hover:text-white/70",
										)}
									>
										<XIcon className="size-3" />
										Incorrect
									</button>
								</div>
								{xResearchStatus === "incorrect" && (
									<>
										<p className="text-xs text-white/40 pt-1">
											If incorrect, share your info in the input below, or you
											can add memories later as well.
										</p>
										<Button
											type="button"
											onClick={handleConfirmDocs}
											disabled={isConfirmed || isLoading}
											className="w-full bg-[#267BF1] hover:bg-[#1E6AD9] text-white rounded-lg py-2 text-sm cursor-pointer mt-2 disabled:opacity-50 disabled:cursor-not-allowed"
										>
											Continue
										</Button>
									</>
								)}
							</div>
						</div>
					)}

					{!draftDocs.some((d) => d.kind === "x_research") &&
						draftDocs.length > 0 &&
						!isConfirmed && (
							<div className="px-4 pb-2">
								<Button
									type="button"
									onClick={handleConfirmDocs}
									disabled={isConfirmed || isLoading}
									className="w-full bg-[#267BF1] hover:bg-[#1E6AD9] text-white rounded-lg py-2 text-sm cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
								>
									Continue
								</Button>
							</div>
						)}

					<div className="p-4 space-y-2">
						{isFetchingDrafts && (
							<div className="flex items-center gap-2 text-white/50 px-2">
								<NovaOrb size={20} className="blur-none!" />
								<span className="text-sm">
									Getting all relevant info about you...
								</span>
							</div>
						)}
						<form
							className="flex flex-col gap-3 bg-[#0D121A] rounded-xl p-2 relative"
							onSubmit={(e) => {
								e.preventDefault()
								if (message.trim()) {
									handleSend()
								}
							}}
						>
							<input
								value={message}
								onChange={(e) => setMessage(e.target.value)}
								onKeyDown={handleKeyDown}
								placeholder="Chat with your Supermemory"
								className="w-full text-white placeholder:text-white/20 rounded-sm outline-none resize-none text-base leading-relaxed bg-transparent px-2 h-10"
								disabled={status === "submitted" || status === "streaming"}
							/>
							<div className="flex justify-end absolute bottom-3 right-2">
								<Button
									type="submit"
									disabled={
										!message.trim() ||
										status === "submitted" ||
										status === "streaming"
									}
									className="text-white/20 hover:text-white disabled:opacity-50 disabled:cursor-not-allowed rounded-xl transition-all"
									size="icon"
								>
									<SendIcon className="size-4" />
								</Button>
							</div>
						</form>
					</div>
				</motion.div>
			)}
		</AnimatePresence>
	)
}
