"use client"

import { Dialog, DialogContent, DialogTitle } from "@repo/ui/components/dialog"
import { Drawer, DrawerContent, DrawerTitle } from "@repo/ui/components/drawer"
import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/components/tabs"
import {
	ArrowUpRightIcon,
	XIcon,
	Loader2,
	Trash2Icon,
	CheckIcon,
	CopyIcon,
} from "lucide-react"
import type { z } from "zod"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { cn } from "@lib/utils"
import { Title } from "./title"
import { Summary as DocumentSummary } from "./summary"
import { dmSansClassName } from "@/lib/fonts"
import { GraphListMemories, type MemoryEntry } from "./graph-list-memories"
import { DocumentContent } from "./content"
import { useState, useEffect, useCallback, useMemo } from "react"
import { motion, AnimatePresence } from "motion/react"
import { useDocumentMutations } from "@/hooks/use-document-mutations"
import type { UseMutationResult } from "@tanstack/react-query"
import { toast } from "sonner"
import { useIsMobile } from "@hooks/use-mobile"
import { parsePluginDocument } from "@/lib/plugin-document"
import { useFullDocumentContent } from "@/hooks/use-full-document"

type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema>
type DocumentWithMemories = DocumentsResponse["documents"][0]

function getDocumentSourceUrl(document: DocumentWithMemories): string {
	const url = document.url ?? ""
	const googleDocTypes: Record<string, string> = {
		google_doc: "https://docs.google.com/document/d/",
		google_sheet: "https://docs.google.com/spreadsheets/d/",
		google_slide: "https://docs.google.com/presentation/d/",
	}

	const prefix = document.type ? googleDocTypes[document.type] : null
	if (!prefix) return url

	if (document.customId) {
		return `${prefix}${document.customId}/edit`
	}

	// Extract ID from API URL like docs.googleapis.com/v1/documents/{id}
	const apiMatch = url.match(
		/docs\.googleapis\.com\/v1\/documents\/([a-zA-Z0-9_-]+)/,
	)
	if (apiMatch?.[1]) {
		return `${prefix}${apiMatch[1]}/edit`
	}

	return url
}

interface DocumentModalProps {
	document: DocumentWithMemories | null
	isOpen: boolean
	onClose: () => void
}

interface DeleteButtonProps {
	documentId: string | null | undefined
	customId: string | null | undefined
	deleteMutation: UseMutationResult<
		unknown,
		Error,
		{ documentId: string },
		unknown
	>
}

function isTemporaryId(id: string | null | undefined): boolean {
	if (!id) return false
	return id.startsWith("temp-") || id.startsWith("temp-file-")
}

function DeleteButton({
	documentId,
	customId,
	deleteMutation,
}: DeleteButtonProps) {
	const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false)

	const handleDelete = useCallback(() => {
		const id = documentId ?? customId
		if (!id) return

		// Check both IDs to ensure we catch temporary documents regardless of which ID is used
		if (isTemporaryId(documentId) || isTemporaryId(customId)) {
			// this is when user added document immediately and trying to delete
			toast.error("Cannot delete document", {
				description: "This document is still being processed. Please wait.",
			})
			return
		}

		deleteMutation.mutate({ documentId: id as string })
	}, [documentId, customId, deleteMutation])

	return (
		<AnimatePresence mode="wait">
			{!deleteConfirmOpen ? (
				<motion.button
					key="trash"
					type="button"
					initial={{ opacity: 0, scale: 0.8 }}
					animate={{ opacity: 1, scale: 1 }}
					exit={{ opacity: 0, scale: 0.8 }}
					transition={{ duration: 0.15 }}
					onClick={() => setDeleteConfirmOpen(true)}
					tabIndex={-1}
					className="bg-[#0D121A] size-7 flex items-center justify-center rounded-full transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-offset-2 focus:outline-none cursor-pointer shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]"
					disabled={deleteMutation.isPending}
				>
					<Trash2Icon className="size-4 text-red-500" />
					<span className="sr-only">Delete document</span>
				</motion.button>
			) : (
				<motion.div
					key="confirm"
					initial={{ opacity: 0, scale: 0.8 }}
					animate={{ opacity: 1, scale: 1 }}
					exit={{ opacity: 0, scale: 0.8 }}
					transition={{ duration: 0.15 }}
					className="flex items-center gap-1 px-1 bg-[#0D121A] rounded-full shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]"
				>
					<button
						type="button"
						onClick={() => setDeleteConfirmOpen(false)}
						disabled={deleteMutation.isPending}
						className="size-6 flex items-center justify-center rounded-full transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-offset-2 focus:outline-none cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
					>
						<XIcon className="size-4 text-[#737373]" />
						<span className="sr-only">Cancel delete</span>
					</button>
					<button
						type="button"
						onClick={handleDelete}
						disabled={deleteMutation.isPending}
						className="size-6 flex items-center justify-center rounded-full transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-offset-2 focus:outline-none cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
					>
						{deleteMutation.isPending ? (
							<Loader2 className="size-4 text-green-500 animate-spin" />
						) : (
							<CheckIcon className="size-4 text-green-500" />
						)}
						<span className="sr-only">Confirm delete</span>
					</button>
				</motion.div>
			)}
		</AnimatePresence>
	)
}

function CopySessionIdButton({ sessionId }: { sessionId: string }) {
	const [copied, setCopied] = useState(false)

	const handleCopy = useCallback(async () => {
		try {
			await navigator.clipboard.writeText(sessionId)
			setCopied(true)
			toast.success("Copy session id")
			setTimeout(() => setCopied(false), 1500)
		} catch {
			toast.error("Failed to copy session id")
		}
	}, [sessionId])

	return (
		<button
			type="button"
			onClick={handleCopy}
			tabIndex={-1}
			title="Copy session id"
			aria-label="Copy session id"
			className="bg-[#0D121A] w-7 h-7 flex items-center justify-center rounded-full transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-offset-2 focus:outline-none cursor-pointer shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]"
		>
			{copied ? (
				<CheckIcon className="w-4 h-4 text-green-500" />
			) : (
				<CopyIcon className="w-4 h-4 text-[#737373]" />
			)}
			<span className="sr-only">Copy session id</span>
		</button>
	)
}

export function DocumentModal({
	document: _document,
	isOpen,
	onClose,
}: DocumentModalProps) {
	const isMobile = useIsMobile()
	const { updateMutation, deleteMutation } = useDocumentMutations({ onClose })

	const fullContent = useFullDocumentContent(_document?.id, isOpen)
	const effectiveDocument = useMemo(
		() =>
			_document && fullContent !== null
				? { ..._document, content: fullContent }
				: _document,
		[_document, fullContent],
	)

	const { initialEditorContent, initialEditorString } = useMemo(() => {
		const content = effectiveDocument?.content as string | null | undefined
		return {
			initialEditorContent: content ?? undefined,
			initialEditorString: content ?? "",
		}
	}, [effectiveDocument?.content])
	const pluginDocument = useMemo(
		() => parsePluginDocument(effectiveDocument),
		[effectiveDocument],
	)

	const [draftContentString, setDraftContentString] =
		useState(initialEditorString)
	const [editorResetNonce, setEditorResetNonce] = useState(0)
	const [lastSavedContent, setLastSavedContent] = useState<string | null>(null)

	const resetEditor = useCallback(() => {
		setDraftContentString(initialEditorString)
		setEditorResetNonce((n) => n + 1)
		setLastSavedContent(null)
	}, [initialEditorString])

	useEffect(() => {
		setDraftContentString(initialEditorString)
		setEditorResetNonce((n) => n + 1)
		setLastSavedContent(null)
	}, [initialEditorString])

	useEffect(() => {
		if (!isOpen) {
			resetEditor()
		}
	}, [isOpen, resetEditor])

	const hasUnsavedChanges =
		draftContentString !== initialEditorString &&
		draftContentString !== lastSavedContent

	const handleSave = useCallback(() => {
		if (!_document?.id) return
		updateMutation.mutate(
			{ documentId: _document.id, content: draftContentString },
			{
				onSuccess: (_data, variables) => setLastSavedContent(variables.content),
			},
		)
	}, [_document?.id, draftContentString, updateMutation])

	const textEditorProps = useMemo(
		() => ({
			documentId: _document?.id ?? "",
			editorResetNonce,
			initialEditorContent,
			hasUnsavedChanges,
			isSaving: updateMutation.isPending,
			onContentChange: setDraftContentString,
			onSave: handleSave,
			onReset: resetEditor,
		}),
		[
			_document?.id,
			editorResetNonce,
			initialEditorContent,
			hasUnsavedChanges,
			updateMutation.isPending,
			handleSave,
			resetEditor,
		],
	)

	const hasDocumentInsights = Boolean(
		_document?.summary ||
			pluginDocument?.summary ||
			(_document?.memoryEntries && _document.memoryEntries.length > 0),
	)

	const documentPreview = (
		<div
			id="document-preview"
			className="bg-[#14161A] rounded-[14px] overflow-hidden flex min-h-0 flex-1 flex-col shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)] relative"
		>
			<DocumentContent
				document={effectiveDocument}
				textEditorProps={textEditorProps}
				pluginDocument={pluginDocument}
			/>
		</div>
	)

	const documentInsights = (
		<div
			id="document-memories-summary"
			className={cn(
				"gap-3 flex flex-col overflow-hidden",
				isMobile && "min-h-0 flex-1",
				dmSansClassName(),
			)}
		>
			{_document && (_document.summary || pluginDocument?.summary) && (
				<DocumentSummary
					memoryEntries={_document.memoryEntries}
					summary={(pluginDocument?.summary ?? _document.summary) as string}
					createdAt={_document.createdAt}
				/>
			)}
			{_document?.memoryEntries && _document.memoryEntries.length > 0 && (
				<GraphListMemories
					memoryEntries={_document.memoryEntries as MemoryEntry[]}
					documentId={_document.id}
					className={
						isMobile ? "h-auto min-h-[420px] flex-1 shrink" : undefined
					}
				/>
			)}
		</div>
	)

	const modalContent = (
		<>
			{isMobile ? (
				<DrawerTitle className="sr-only">
					{_document?.title} - Document
				</DrawerTitle>
			) : (
				<DialogTitle className="sr-only">
					{_document?.title} - Document
				</DialogTitle>
			)}
			<div className="flex items-center justify-between h-fit gap-2 md:gap-4">
				<div className="flex-1 min-w-0">
					<Title
						title={_document?.title}
						documentType={_document?.type ?? "text"}
						url={_document?.url}
						pluginIconSrc={pluginDocument?.pluginIconSrc}
					/>
				</div>
				<div className="flex items-center gap-1.5 md:gap-2 shrink-0">
					{pluginDocument?.kind === "claude-code-doc" &&
						_document?.customId && (
							<CopySessionIdButton sessionId={_document.customId} />
						)}
					<DeleteButton
						documentId={_document?.id}
						customId={_document?.customId}
						deleteMutation={deleteMutation}
					/>
					{_document?.url && (
						<a
							href={getDocumentSourceUrl(_document)}
							target="_blank"
							rel="noopener noreferrer"
							className={cn(
								"flex items-center gap-1 bg-[#0D121A] rounded-full shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]",
								isMobile ? "size-7 justify-center" : "px-3 py-2",
							)}
						>
							{!isMobile && <span className="line-clamp-1">Visit source</span>}
							<ArrowUpRightIcon className="size-4 text-[#737373]" />
						</a>
					)}
					{isMobile ? (
						<button
							className="bg-[#0D121A] size-7 flex items-center justify-center rounded-full transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus:outline-none disabled:pointer-events-none cursor-pointer [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]"
							type="button"
							tabIndex={-1}
							onClick={onClose}
						>
							<XIcon stroke="#737373" />
							<span className="sr-only">Close</span>
						</button>
					) : (
						<DialogPrimitive.Close
							className="bg-[#0D121A] size-7 flex items-center justify-center rounded-full transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus:outline-none disabled:pointer-events-none cursor-pointer [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 shadow-[inset_0_2px_4px_rgba(0,0,0,0.3),inset_0_1px_2px_rgba(0,0,0,0.1)]"
							data-slot="dialog-close"
							type="button"
							tabIndex={-1}
						>
							<XIcon stroke="#737373" />
							<span className="sr-only">Close</span>
						</DialogPrimitive.Close>
					)}
				</div>
			</div>
			{isMobile && hasDocumentInsights ? (
				<Tabs
					defaultValue="content"
					className="flex min-h-0 flex-1 flex-col pt-1.5"
				>
					<TabsList className="grid h-11 w-full grid-cols-2 rounded-full border border-[#263142] bg-[#0A1019] p-1 shadow-[inset_0_1px_2px_rgba(255,255,255,0.04),0_1px_3px_rgba(0,0,0,0.35)]">
						<TabsTrigger
							value="content"
							className="rounded-full text-[15px] font-medium text-[#8E99AA] transition-colors data-[state=active]:bg-[#0B2B60]! data-[state=active]:text-[#F8FAFC] data-[state=active]:shadow-[inset_0_1px_1px_rgba(255,255,255,0.08),0_1px_4px_rgba(54,155,253,0.18)]"
						>
							Content
						</TabsTrigger>
						<TabsTrigger
							value="insights"
							className="rounded-full text-[15px] font-medium text-[#8E99AA] transition-colors data-[state=active]:bg-[#0B2B60]! data-[state=active]:text-[#F8FAFC] data-[state=active]:shadow-[inset_0_1px_1px_rgba(255,255,255,0.08),0_1px_4px_rgba(54,155,253,0.18)]"
						>
							Insights
						</TabsTrigger>
					</TabsList>
					<TabsContent value="content" className="mt-4 flex min-h-0 flex-1">
						{documentPreview}
					</TabsContent>
					<TabsContent
						value="insights"
						className="mt-4 flex min-h-0 flex-1 flex-col overflow-y-auto pb-1 scrollbar-thin"
					>
						{documentInsights}
					</TabsContent>
				</Tabs>
			) : isMobile ? (
				<div className="flex min-h-0 flex-1 pt-1.5">{documentPreview}</div>
			) : (
				<div className="flex-1 grid grid-cols-1 md:grid-cols-[2fr_1fr] gap-3 min-h-0 overflow-hidden">
					{documentPreview}
					{documentInsights}
				</div>
			)}
		</>
	)

	if (isMobile) {
		return (
			<Drawer
				open={isOpen}
				onOpenChange={(open: boolean) => !open && onClose()}
				shouldScaleBackground
			>
				<DrawerContent
					className={cn(
						"flex flex-col gap-0 border-none bg-[#1B1F24] p-0",
						"h-[88svh] max-h-[88svh] overflow-hidden rounded-t-[22px]",
						"[&>div:first-child]:bg-[#3A4252] [&>div:first-child]:h-1 [&>div:first-child]:w-9 [&>div:first-child]:mt-2.5 [&>div:first-child]:mb-1",
						dmSansClassName(),
					)}
					style={{
						boxShadow:
							"0 -12px 40px rgba(0, 0, 0, 0.45), 0.711px 0.711px 0.711px 0 rgba(255, 255, 255, 0.10) inset",
					}}
				>
					<div className="flex min-h-0 flex-1 flex-col gap-3 overflow-hidden px-3 pt-2 pb-4">
						{modalContent}
					</div>
				</DrawerContent>
			</Drawer>
		)
	}

	return (
		<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
			<DialogContent
				className={cn(
					"p-0 border-none bg-[#1B1F24] flex flex-col px-3 md:px-4 pt-3 pb-4 gap-3",
					"w-[80%]! max-w-[1158px]! h-[86%]! max-h-[684px]! rounded-[22px]",
					dmSansClassName(),
				)}
				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",
				}}
				showCloseButton={false}
			>
				{modalContent}
			</DialogContent>
		</Dialog>
	)
}
