"use client";

import { AnimatePresence, motion } from "framer-motion";
import { Bot, ImageIcon, Info, Loader2, Sparkles, X } from "lucide-react";
import Image from "next/image";
import { useEffect, useRef, useState } from "react";
import { ImagePicker } from "@/components/image-picker";
import { Button } from "@/components/ui/button";
import {
	HoverCard,
	HoverCardContent,
	HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Kbd, KbdGroup } from "@/components/ui/kbd";
import {
	Popover,
	PopoverContent,
	PopoverTrigger,
} from "@/components/ui/popover";
import { Skeleton } from "@/components/ui/skeleton";
import { Textarea } from "@/components/ui/textarea";
import { useIsMobile } from "@/hooks/use-mobile";
import { extractAllImagesFromClipboard } from "@/lib/clipboard-image";
import {
	detectOs,
	type Os,
	resolveKeys,
	SHORTCUT_DEFINITIONS,
} from "@/lib/keyboard-shortcuts";
import type { CalendarEvent } from "@/lib/types";

// ─── OS detection hook ────────────────────────────────────────────────────────

function useOs(): Os {
	// Start with "unknown" for SSR — effect sets the real value after hydration
	const [os, setOs] = useState<Os>("unknown");
	useEffect(() => {
		setOs(detectOs());
	}, []);
	return os;
}

async function getClipboardBlobIdentity(blob: Blob): Promise<string> {
	const bytes = new Uint8Array(await blob.arrayBuffer());
	let hash = 2166136261;

	for (const byte of bytes) {
		hash ^= byte;
		hash = Math.imul(hash, 16777619);
	}

	return (hash >>> 0).toString(16).padStart(8, "0");
}

async function normalizeClipboardImageFile(blob: Blob): Promise<File> {
	const identity = await getClipboardBlobIdentity(blob);
	return new File([blob], `clipboard-image-${identity}`, {
		type: blob.type,
		lastModified: 0,
	});
}

async function normalizeClipboardImageFiles(files: File[]): Promise<File[]> {
	return Promise.all(files.map((file) => normalizeClipboardImageFile(file)));
}

// ─── Shared shortcuts list (rendered in both HoverCard and Popover) ───────────

function ShortcutsList({ os }: { os: Os }) {
	return (
		<>
			<p className="text-xs font-semibold text-foreground mb-2.5">
				Keyboard shortcuts
			</p>
			<ul className="space-y-2">
				{SHORTCUT_DEFINITIONS.map((shortcut) => (
					<li
						key={shortcut.label}
						className="flex items-center justify-between gap-3"
					>
						<span className="text-xs text-muted-foreground">
							{shortcut.label}
						</span>
						<KbdGroup>
							{resolveKeys(shortcut.modifiers, os).map((key) => (
								<Kbd key={key}>{key}</Kbd>
							))}
						</KbdGroup>
					</li>
				))}
			</ul>
		</>
	);
}

function isEditableTarget(target: EventTarget | null): target is HTMLElement {
	const element = target as HTMLElement | null;
	return (
		!!element &&
		(element.tagName === "TEXTAREA" ||
			element.tagName === "INPUT" ||
			element.isContentEditable)
	);
}

// ─── Types ────────────────────────────────────────────────────────────────────

interface AIToolbarProps {
	adminAiEnabled: boolean;
	aiEnabled: boolean;
	isAuthenticated: boolean;
	isPending: boolean;
	aiPrompt: string;
	setAiPrompt: (prompt: string) => void;
	aiLoading: boolean;
	/** Ordered list of object-URL preview strings for each attached image. */
	imagePreviews: string[];
	/** Called with one or more new files to append (dedup handled by parent). */
	onImagesSelect: (files: File[]) => void;
	/** Remove the image at the given index from the list. */
	onImageRemove: (index: number) => void;
	onAiCreate: () => void;
	onAiTemplateSelect: (prompt: string) => void;
	onAiSummarize: () => void;
	onSummaryDismiss: () => void;
	summary: string | null;
	summaryUpdated: string | null;
	events: CalendarEvent[];
}

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

export const AIToolbar = ({
	adminAiEnabled,
	aiEnabled,
	isAuthenticated,
	isPending,
	aiPrompt,
	setAiPrompt,
	aiLoading,
	imagePreviews,
	onImagesSelect,
	onImageRemove,
	onAiCreate,
	onAiTemplateSelect,
	onAiSummarize,
	onSummaryDismiss,
	summary,
	summaryUpdated,
	events,
}: AIToolbarProps) => {
	const isMobile = useIsMobile();
	const examplePrompts = [
		"Lunch with Maya next Thursday at 12:30pm at Toma, remind me 30 minutes before.",
		"Project sync tomorrow from 9am to 10am on Google Meet with a weekly repeat.",
		"Dentist appointment on May 14 at 3pm at Smile Studio, add confirmation #A4821.",
	];
	const canUseAi = adminAiEnabled && aiEnabled;

	// Ref to imperatively open the file picker from the keyboard shortcut
	const imageTriggerRef = useRef<{ open: () => void }>(null);

	// When the popover is pinned open, suppress the hover card so they don't overlap
	const [isPopoverOpen, setIsPopoverOpen] = useState(false);

	// Detect OS after hydration for keyboard shortcut glyphs
	const os = useOs();

	// Stable ref so the document listener never needs to re-register when
	// onImagesSelect identity changes between renders (it's an inline async fn).
	const onImagesSelectRef = useRef(onImagesSelect);
	useEffect(() => {
		onImagesSelectRef.current = onImagesSelect;
	}, [onImagesSelect]);

	// Document-level paste + Ctrl/Cmd+V keydown handler.
	//
	// Two-pronged approach because Linux/Chrome does not reliably include image
	// data in clipboardData on trusted paste events when no input is focused:
	//
	//   1. paste event   — works when the textarea IS focused (clipboardData has
	//                      the image). The textarea's own onPaste handles that
	//                      case; here we only handle non-editable targets.
	//
	//   2. keydown Ctrl+V — user gesture that explicitly reads the async
	//                      Clipboard API (navigator.clipboard.read()), which
	//                      always has the full clipboard contents regardless of
	//                      focused element or OS clipboard model (X11/Wayland).
	//                      This is the approach used by Excalidraw's actionPaste.
	useEffect(() => {
		if (!(isAuthenticated && !isPending && canUseAi && !aiLoading)) return;

		// ── Handler 1: paste event (works when textarea is NOT focused) ───────
		const handleDocumentPaste = (e: ClipboardEvent) => {
			if (isEditableTarget(e.target)) return; // textarea's own onPaste covers this

			const images = extractAllImagesFromClipboard(e.clipboardData ?? null);
			if (images.length > 0) {
				e.preventDefault();
				void normalizeClipboardImageFiles(images).then((normalizedImages) => {
					onImagesSelectRef.current(normalizedImages);
				});
			}
		};

		// ── Handler 2: keydown Ctrl/Cmd+V → async Clipboard API fallback ─────
		// On Linux/Chrome, clipboardData is often empty in paste events when the
		// clipboard was set by an external app. navigator.clipboard.read() is
		// more reliable when called from a user gesture (keydown).
		let pasteHandledByEvent = false;

		const PROBE_TYPES = [
			"image/png",
			"image/jpeg",
			"image/webp",
			"image/gif",
			"image/bmp",
			"image/tiff",
		];

		const handleDocumentKeydown = async (e: KeyboardEvent) => {
			const isV = e.key === "v" || e.key === "V";
			const isModifier = e.ctrlKey || e.metaKey;
			if (!isV || !isModifier || e.shiftKey || e.altKey) return;
			if (e.ctrlKey && e.metaKey) return;
			if (isEditableTarget(e.target)) return;

			pasteHandledByEvent = false;

			// Defer one tick so the synchronous paste event can fire first and
			// set pasteHandledByEvent if it already handled an image.
			await new Promise<void>((r) => setTimeout(r, 0));
			if (pasteHandledByEvent) return;

			try {
				const clipboardItems = await navigator.clipboard.read();
				const files: File[] = [];

				for (const clipboardItem of clipboardItems) {
					const declaredType = clipboardItem.types.find((t) =>
						t.startsWith("image/"),
					);
					const typesToTry = declaredType
						? [declaredType, ...PROBE_TYPES.filter((t) => t !== declaredType)]
						: PROBE_TYPES;

					for (const mimeType of typesToTry) {
						try {
							const blob = await clipboardItem.getType(mimeType);
							files.push(await normalizeClipboardImageFile(blob));
							break; // got this item, move to next clipboardItem
						} catch {
							// NotFoundError — type not present, try next
						}
					}
				}

				if (files.length > 0) {
					onImagesSelectRef.current(files);
				}
			} catch {
				// clipboard.read() failed (permissions denied, etc.) — ignore
			}
		};

		// Mark that the synchronous paste event handled images so keydown
		// doesn't double-fire
		const handlePasteHandled = (e: ClipboardEvent) => {
			if (isEditableTarget(e.target)) return;

			const images = extractAllImagesFromClipboard(e.clipboardData ?? null);
			if (images.length > 0) pasteHandledByEvent = true;
		};

		document.addEventListener("paste", handleDocumentPaste);
		document.addEventListener("paste", handlePasteHandled, { capture: true });
		document.addEventListener("keydown", handleDocumentKeydown);

		return () => {
			document.removeEventListener("paste", handleDocumentPaste);
			document.removeEventListener("paste", handlePasteHandled, {
				capture: true,
			});
			document.removeEventListener("keydown", handleDocumentKeydown);
		};
	}, [isAuthenticated, isPending, canUseAi, aiLoading]); // onImagesSelect intentionally omitted — ref stays current

	if (isPending) {
		return (
			<div className="space-y-3">
				<Skeleton className="h-[160px] w-full rounded-2xl" />
				<Skeleton className="h-12 w-full rounded-2xl" />
			</div>
		);
	}

	const hasImages = imagePreviews.length > 0;
	const showDisabledState = isAuthenticated && !canUseAi;

	return (
		<div className="space-y-3">
			{showDisabledState ? (
				<div className="flex items-center gap-3 py-2">
					<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground">
						<Sparkles className="h-4 w-4" />
					</div>
					<div className="min-w-0 flex-1">
						<p className="text-sm font-medium leading-tight text-foreground">
							AI integrations are unavailable
						</p>
						<p className="mt-0.5 text-sm leading-relaxed text-muted-foreground">
							{adminAiEnabled
								? "AI has been turned off in this browser from Settings."
								: "AI integrations are currently disabled by the administrator."}
						</p>
					</div>
				</div>
			) : isAuthenticated ? (
				<div
					className={
						isMobile
							? "grid gap-3"
							: "grid gap-3 grid-cols-[minmax(0,0.7fr)_minmax(0,0.3fr)]"
					}
				>
					<div className="space-y-3">
						<div className="rounded-[10px] bg-card shadow focus-within:ring-[3px] focus-within:ring-ring/20">
							<Textarea
								id="ai-event-prompt"
								className="wrap-anywhere field-sizing-content min-h-48 w-full resize-none rounded-none border-0 bg-transparent px-4 py-3 text-sm shadow-none placeholder:text-muted-foreground/50 focus-visible:ring-0 focus-visible:ring-offset-0"
								placeholder="Type or paste event details..."
								value={aiPrompt}
								onChange={(e) => setAiPrompt(e.target.value)}
								onKeyDown={(e) => {
									if (
										e.key === "Enter" &&
										(e.metaKey || e.ctrlKey) &&
										!(e.metaKey && e.ctrlKey) &&
										!e.shiftKey &&
										!e.altKey &&
										!aiLoading &&
										canUseAi &&
										(aiPrompt.trim() || hasImages)
									) {
										e.preventDefault();
										onAiCreate();
									}
									if (
										e.key === "A" &&
										e.shiftKey &&
										(e.metaKey || e.ctrlKey) &&
										!(e.metaKey && e.ctrlKey) &&
										!e.altKey &&
										!aiLoading
									) {
										e.preventDefault();
										imageTriggerRef.current?.open();
									}
									if (e.key === "Escape" && aiPrompt) {
										e.preventDefault();
										setAiPrompt("");
									}
								}}
								onPaste={(e) => {
									const images = extractAllImagesFromClipboard(
										e.clipboardData ?? null,
									);
									if (images.length > 0) {
										e.preventDefault();
										void normalizeClipboardImageFiles(images).then(
											(normalizedImages) => {
												onImagesSelect(normalizedImages);
											},
										);
									}
								}}
							/>
							<div className="border-t border-border px-3 py-3">
								<div className="space-y-2">
									<span className="text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground">
										Try:
									</span>
									<div className="columns-2 gap-2">
										{examplePrompts.map((prompt) => (
											<div key={prompt} className="mb-2 break-inside-avoid">
												<Button
													type="button"
													variant="secondary"
													size="sm"
													className="h-auto w-full justify-start text-left whitespace-normal rounded-2xl px-3 py-2 text-[11px] leading-relaxed"
													onClick={() => onAiTemplateSelect(prompt)}
													disabled={aiLoading || !canUseAi}
												>
													{prompt}
												</Button>
											</div>
										))}
									</div>
								</div>
							</div>
						</div>

						<div className="flex flex-wrap items-center justify-between gap-2">
							<div className="flex items-center gap-1.5">
								{!isMobile ? (
									<HoverCard
										openDelay={300}
										closeDelay={100}
										open={isPopoverOpen ? false : undefined}
									>
										<Popover
											open={isPopoverOpen}
											onOpenChange={setIsPopoverOpen}
										>
											<HoverCardTrigger asChild>
												<PopoverTrigger asChild>
													<Button
														variant="ghost"
														size="icon"
														className="h-8 w-8 text-muted-foreground/70 hover:text-foreground"
														aria-label="Keyboard shortcuts"
													>
														<Info className="h-3.5 w-3.5" />
													</Button>
												</PopoverTrigger>
											</HoverCardTrigger>
											<PopoverContent
												align="start"
												side="top"
												sideOffset={6}
												className="w-52 p-3"
											>
												<ShortcutsList os={os} />
											</PopoverContent>
										</Popover>
										<HoverCardContent
											align="start"
											side="top"
											sideOffset={6}
											className="w-52 p-3"
										>
											<ShortcutsList os={os} />
										</HoverCardContent>
									</HoverCard>
								) : null}

								{events.length > 0 && (
									<Button
										variant="ghost"
										size="sm"
										onClick={onAiSummarize}
										disabled={aiLoading || !canUseAi}
										className="h-9 gap-1.5 px-3 text-xs text-muted-foreground hover:text-primary"
									>
										<Bot className="h-3 w-3" />
										Summarize
									</Button>
								)}
							</div>

							<Button
								size="sm"
								className="h-10 gap-1.5 px-4 text-xs"
								onClick={onAiCreate}
								disabled={
									aiLoading || !canUseAi || (!aiPrompt.trim() && !hasImages)
								}
							>
								{aiLoading ? (
									<Loader2 className="h-3.5 w-3.5 animate-spin" />
								) : (
									<Sparkles className="h-3.5 w-3.5" />
								)}
								{aiLoading ? "Generating..." : "Generate event"}
							</Button>
						</div>
					</div>

					<div className="rounded-[10px] bg-card p-3 shadow-sm">
						<div className="mb-3 flex items-start justify-between gap-3">
							<div>
								<p className="text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground">
									Attachments
								</p>
								<p className="mt-1 text-sm leading-6 text-muted-foreground">
									Add screenshots, flyers, or pasted images alongside the
									prompt.
								</p>
							</div>
							<span className="rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground">
								{imagePreviews.length} file
								{imagePreviews.length === 1 ? "" : "s"}
							</span>
						</div>

						<ImagePicker
							onFilesSelect={onImagesSelect}
							disabled={aiLoading || !canUseAi}
							multiple
							variant="outline"
							size="sm"
							className="h-10 w-full justify-center gap-2"
							triggerRef={imageTriggerRef}
						>
							<ImageIcon className="h-4 w-4" />
							Attach images
						</ImagePicker>

						<AnimatePresence>
							{hasImages ? (
								<motion.div
									initial={{ opacity: 0, y: 4 }}
									animate={{ opacity: 1, y: 0 }}
									exit={{ opacity: 0, y: 4 }}
									transition={{ duration: 0.15 }}
									className="mt-3 grid gap-2"
								>
									{imagePreviews.map((preview, index) => (
										<motion.div
											key={preview}
											initial={{ opacity: 0, scale: 0.96 }}
											animate={{ opacity: 1, scale: 1 }}
											exit={{ opacity: 0, scale: 0.96 }}
											transition={{ duration: 0.12 }}
											className="relative overflow-hidden rounded-[8px] bg-muted"
										>
											<Image
												src={preview}
												alt={`Attached image ${index + 1}`}
												className="h-32 w-full object-cover"
												width={256}
												height={160}
												unoptimized
											/>
											<Button
												variant="destructive"
												size="icon"
												className="absolute top-2 right-2 h-7 w-7 rounded-full"
												onClick={() => onImageRemove(index)}
												aria-label={`Remove image ${index + 1}`}
											>
												<X className="h-3 w-3" />
											</Button>
										</motion.div>
									))}
								</motion.div>
							) : (
								<div className="mt-3 rounded-[8px] border border-dashed border-border px-3 py-6 text-center text-sm text-muted-foreground">
									Drop or paste images here to pair them with the prompt.
								</div>
							)}
						</AnimatePresence>
					</div>
				</div>
			) : (
				<div className="flex items-center gap-3 py-2">
					<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
						<Sparkles className="h-4 w-4" />
					</div>
					<div className="flex-1 min-w-0">
						<p className="text-sm font-medium text-foreground leading-tight">
							Sign in required to generate event drafts with AI
						</p>
						<p className="text-xs text-muted-foreground mt-0.5">
							Sign in to turn natural language or flyers into event drafts, then
							review or save them from your calendar workflow.
						</p>
					</div>
				</div>
			)}

			{/* ── AI Summary panel ─────────────────────────────────────────────────── */}
			<AnimatePresence>
				{summary && (
					<motion.div
						initial={{ opacity: 0, y: -4, height: 0 }}
						animate={{ opacity: 1, y: 0, height: "auto" }}
						exit={{ opacity: 0, y: -4, height: 0 }}
						transition={{ duration: 0.2, ease: "easeOut" }}
						className="overflow-hidden"
					>
						<div className="rounded-2xl border border-primary/15 bg-primary/5 p-3.5">
							<div className="flex items-start justify-between gap-2 mb-2">
								<div className="flex items-center gap-1.5">
									<Bot className="h-3.5 w-3.5 text-primary shrink-0 mt-px" />
									<span className="text-xs font-semibold text-primary">
										AI Summary
									</span>
								</div>
								<div className="flex items-center gap-2 shrink-0">
									{summaryUpdated && (
										<span className="text-xs text-muted-foreground/60">
											{summaryUpdated}
										</span>
									)}
									<Button
										variant="ghost"
										size="icon"
										className="h-5 w-5 text-muted-foreground hover:text-foreground"
										onClick={onSummaryDismiss}
										aria-label="Dismiss AI summary"
									>
										<X className="h-3 w-3" />
									</Button>
								</div>
							</div>
							<p className="text-sm leading-relaxed text-foreground/90">
								{summary}
							</p>
						</div>
					</motion.div>
				)}
			</AnimatePresence>
		</div>
	);
};
