/**
 * GifPicker — Klipy-backed GIF browser embedded in the EmojiPicker's
 * "GIFs" tab. Layout matches the new UI screenshot:
 *
 *   ┌────────────────────────────────────┐
 *   │ [ 🔍 Search Tenor / Klipy   ]      │
 *   │ ┌──────────┐  ┌──────────────────┐ │
 *   │ │ ★        │  │  Trending GIFs   │ │
 *   │ │ Favorites│  │                  │ │
 *   │ └──────────┘  └──────────────────┘ │
 *   │ ┌────┐ ┌────┐ ┌────┐ ┌────┐        │
 *   │ │ G  │ │ G  │ │ G  │ │ G  │        │
 *   │ └────┘ └────┘ └────┘ └────┘        │
 *   └────────────────────────────────────┘
 *
 * Click on any tile fires `onSelectGif(url)` — the parent picker
 * forwards that to the composer, which sends the GIF URL as a
 * plain text message. The auto-link parser in MessageContent +
 * the LinkEmbed/DirectMediaEmbed renderer take it from there.
 */
import { useEffect, useMemo, useState } from 'react';
import { useAction } from 'convex/react';
import { MagnifyingGlass, Star, TrendUp } from '@phosphor-icons/react';
import { api } from '../../../../../convex/_generated/api';
import styles from './GifPicker.module.css';

interface GifResult {
	id: string;
	title: string;
	url: string;
	previewUrl: string;
	width?: number;
	height?: number;
}

interface GifCategory {
	name: string;
	image: string;
	query: string;
}

interface GifPickerProps {
	onSelectGif: (url: string) => void;
}

type Tab = 'home' | 'favorites' | 'trending';

const FAVORITES_KEY = 'gifPicker.favorites';

function loadFavorites(): GifResult[] {
	try {
		const raw = localStorage.getItem(FAVORITES_KEY);
		if (!raw) return [];
		const parsed = JSON.parse(raw);
		return Array.isArray(parsed) ? parsed : [];
	} catch {
		return [];
	}
}

function saveFavorites(items: GifResult[]) {
	try {
		localStorage.setItem(FAVORITES_KEY, JSON.stringify(items.slice(0, 100)));
	} catch {
		/* ignore quota */
	}
}

export function GifPicker({ onSelectGif }: GifPickerProps) {
	const [search, setSearch] = useState('');
	const [tab, setTab] = useState<Tab>('home');
	const [trending, setTrending] = useState<GifResult[]>([]);
	const [searchResults, setSearchResults] = useState<GifResult[]>([]);
	const [categories, setCategories] = useState<GifCategory[]>([]);
	const [favorites, setFavorites] = useState<GifResult[]>(() =>
		loadFavorites(),
	);
	const [loading, setLoading] = useState(false);
	const [error, setError] = useState<string | null>(null);

	const searchAction = useAction(api.gifs.search);
	const trendingAction = useAction(api.gifs.trending);
	const categoriesAction = useAction(api.gifs.categories);

	// Load trending feed + categories once when the picker mounts.
	// Both are cached server-side (convex/gifs.ts in-memory TTL) so
	// toggling tabs or re-opening the picker doesn't re-hit Klipy.
	// The two requests run in parallel so first paint shows the
	// category chips even if trending is still loading.
	useEffect(() => {
		let cancelled = false;
		(async () => {
			setLoading(true);
			setError(null);
			try {
				const [trendingRes, categoriesRes] = await Promise.all([
					trendingAction({ limit: 24 }),
					categoriesAction({}),
				]);
				if (cancelled) return;
				setTrending((trendingRes as any)?.results ?? []);
				setCategories((categoriesRes as any)?.categories ?? []);
			} catch (err: any) {
				if (cancelled) return;
				setError(err?.message ?? 'Failed to load GIFs.');
			} finally {
				if (!cancelled) setLoading(false);
			}
		})();
		return () => {
			cancelled = true;
		};
	}, [trendingAction, categoriesAction]);

	// Debounced search — fires 350ms after the last keystroke so we
	// don't hammer the upstream API on every character.
	useEffect(() => {
		const q = search.trim();
		if (!q) {
			setSearchResults([]);
			return;
		}
		const t = window.setTimeout(async () => {
			setLoading(true);
			setError(null);
			try {
				const res: any = await searchAction({ q, limit: 24 });
				setSearchResults(res?.results ?? []);
			} catch (err: any) {
				setError(err?.message ?? 'Search failed.');
			} finally {
				setLoading(false);
			}
		}, 350);
		return () => window.clearTimeout(t);
	}, [search, searchAction]);

	const handlePick = (gif: GifResult) => {
		onSelectGif(gif.url);
	};

	const toggleFavorite = (gif: GifResult, e: React.MouseEvent) => {
		e.stopPropagation();
		setFavorites((prev) => {
			const exists = prev.some((g) => g.id === gif.id);
			const next = exists
				? prev.filter((g) => g.id !== gif.id)
				: [gif, ...prev];
			saveFavorites(next);
			return next;
		});
	};

	const isFavorited = (gif: GifResult) =>
		favorites.some((g) => g.id === gif.id);

	// Decide which list to render. Searching always wins — once the
	// user types anything, we show the search results regardless of
	// which featured tab was active. `home` is now a distinct surface
	// (featured row + categories grid) and doesn't use `displayList`
	// at all.
	const isSearching = search.trim().length > 0;
	const displayList: GifResult[] = useMemo(() => {
		if (isSearching) return searchResults;
		if (tab === 'favorites') return favorites;
		if (tab === 'trending') return trending;
		return [];
	}, [isSearching, searchResults, tab, favorites, trending]);

	const showFeaturedRow = !isSearching && tab === 'home';
	const showCategories = !isSearching && tab === 'home';

	const handlePickCategory = (category: GifCategory) => {
		// Piping the category name into the search box lets the
		// existing debounced search effect do the work — same code
		// path as typing "happy birthday" by hand, so results are
		// cached and consistent.
		setSearch(category.query || category.name);
	};

	return (
		<div className={styles.root}>
			<div className={styles.searchRow}>
				<div className={styles.searchBar}>
					<MagnifyingGlass
						size={16}
						weight="regular"
						className={styles.searchIcon}
					/>
					<input
						type="text"
						className={styles.searchInput}
						placeholder="Search Klipy"
						value={search}
						onChange={(e) => setSearch(e.target.value)}
						autoFocus
					/>
				</div>
			</div>

			<div className={styles.body}>
			{showFeaturedRow && (
				<div className={styles.featuredRow}>
					<button
						type="button"
						className={`${styles.featuredTile} ${styles.featuredFavorites}`}
						onClick={() => setTab('favorites')}
					>
						<Star size={28} weight="fill" className={styles.featuredIcon} />
						<span className={styles.featuredLabel}>Favorites</span>
					</button>
					<button
						type="button"
						className={`${styles.featuredTile} ${styles.featuredTrending}`}
						onClick={() => setTab('trending')}
					>
						<TrendUp size={28} weight="fill" className={styles.featuredIcon} />
						<span className={styles.featuredLabel}>Trending GIFs</span>
					</button>
				</div>
			)}

			{tab !== 'home' && !isSearching && (
				<div className={styles.subHeaderRow}>
					<button
						type="button"
						className={styles.backLink}
						onClick={() => setTab('home')}
					>
						← Back
					</button>
					<span className={styles.subHeaderTitle}>
						{tab === 'favorites' ? 'Favorites' : 'Trending GIFs'}
					</span>
				</div>
			)}

			{showCategories ? (
				loading && categories.length === 0 ? (
					<div className={styles.statusMessage}>Loading categories…</div>
				) : error && categories.length === 0 ? (
					<div className={styles.statusMessageError}>{error}</div>
				) : categories.length === 0 ? (
					<div className={styles.statusMessage}>No categories to show.</div>
				) : (
					<div className={styles.categoriesGrid}>
						{categories.map((cat) => (
							<button
								key={`${cat.name}-${cat.query}`}
								type="button"
								className={styles.categoryTile}
								onClick={() => handlePickCategory(cat)}
								title={cat.name}
							>
								{cat.image && (
									<img
										src={cat.image}
										alt=""
										className={styles.categoryTileImage}
										loading="lazy"
										draggable={false}
									/>
								)}
								<span className={styles.categoryTileLabel}>{cat.name}</span>
							</button>
						))}
					</div>
				)
			) : loading && displayList.length === 0 ? (
				<div className={styles.statusMessage}>Loading GIFs…</div>
			) : error ? (
				<div className={styles.statusMessageError}>{error}</div>
			) : displayList.length === 0 ? (
				<div className={styles.statusMessage}>
					{isSearching
						? 'No GIFs match your search.'
						: tab === 'favorites'
							? 'No favorites yet. Click the star on a GIF to save it here.'
							: 'No GIFs to show.'}
				</div>
			) : (
				<div className={styles.grid}>
					{displayList.map((gif) => {
						const fav = isFavorited(gif);
						return (
							<button
								key={gif.id}
								type="button"
								className={styles.tile}
								onClick={() => handlePick(gif)}
								title={gif.title || 'GIF'}
							>
								<img
									src={gif.previewUrl || gif.url}
									alt={gif.title || 'GIF'}
									className={styles.tileImage}
									loading="lazy"
									draggable={false}
								/>
								<button
									type="button"
									className={`${styles.favoriteButton} ${
										fav ? styles.favoriteButtonActive : ''
									}`}
									onClick={(e) => toggleFavorite(gif, e)}
									aria-label={fav ? 'Unfavorite GIF' : 'Favorite GIF'}
									title={fav ? 'Unfavorite' : 'Favorite'}
								>
									<Star size={14} weight={fav ? 'fill' : 'regular'} />
								</button>
							</button>
						);
					})}
				</div>
			)}
			</div>
		</div>
	);
}
