/**
 * MobileExpressionPickerSheet — the mobile emoji drawer. Ported from
 * the new UI so the mobile layout matches: segmented tab selector at
 * the top (GIFs / Media / Stickers / Emojis — only Emojis wired),
 * search bar, collapsible category sections, and a bottom icon bar
 * for jump-to-category navigation. Opens from the composer's emoji
 * button when `useIsMobile()` is true, replacing the desktop
 * portal-based picker.
 */
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
	Bicycle,
	BowlFood,
	CaretRight,
	Flag,
	GameController,
	Gif,
	Heart,
	ImageSquare,
	Leaf,
	Magnet,
	MagnifyingGlass,
	Smiley,
	Sticker,
	X,
} from '@phosphor-icons/react';
import { useQuery } from 'convex/react';
import { BottomSheet } from '@discord-clone/ui';
import { api } from '../../../../../convex/_generated/api';
import { emojiToCodepoint, getTwemojiUrl } from '../../utils/twemoji';
import emojiData from '@app/data/emojis.json';
import type { EmojiPickerValue } from './EmojiPicker';
import { GifPicker } from './GifPicker';
import styles from './MobileExpressionPickerSheet.module.css';

interface EmojiEntry {
	names: string[];
	surrogates: string;
}

type EmojiCategory = keyof typeof emojiData;

const CATEGORIES: Array<{
	key: EmojiCategory;
	label: string;
	icon: React.ComponentType<{ size?: number; weight?: 'regular' | 'fill' | 'bold' }>;
}> = [
	{ key: 'people' as EmojiCategory, label: 'People', icon: Smiley },
	{ key: 'nature' as EmojiCategory, label: 'Nature', icon: Leaf },
	{ key: 'food' as EmojiCategory, label: 'Food & Drink', icon: BowlFood },
	{ key: 'activity' as EmojiCategory, label: 'Activities', icon: GameController },
	{ key: 'travel' as EmojiCategory, label: 'Travel & Places', icon: Bicycle },
	{ key: 'objects' as EmojiCategory, label: 'Objects', icon: Magnet },
	{ key: 'symbols' as EmojiCategory, label: 'Symbols', icon: Heart },
	{ key: 'flags' as EmojiCategory, label: 'Flags', icon: Flag },
];

type Tab = 'gifs' | 'media' | 'stickers' | 'emojis';

const TABS: Array<{ id: Tab; label: string; enabled: boolean }> = [
	{ id: 'gifs', label: 'GIFs', enabled: true },
	{ id: 'media', label: 'Media', enabled: true },
	{ id: 'stickers', label: 'Stickers', enabled: false },
	{ id: 'emojis', label: 'Emojis', enabled: true },
];

interface MobileExpressionPickerSheetProps {
	isOpen: boolean;
	onClose: () => void;
	onSelect: (value: EmojiPickerValue) => void;
	initialTab?: Tab;
}

function TwemojiTile({ surrogates }: { surrogates: string }) {
	return (
		<img
			src={getTwemojiUrl(surrogates)}
			alt={surrogates}
			className={styles.emojiImg}
			loading="lazy"
			draggable={false}
		/>
	);
}

export function MobileExpressionPickerSheet({
	isOpen,
	onClose,
	onSelect,
	initialTab = 'emojis',
}: MobileExpressionPickerSheetProps) {
	const [activeTab, setActiveTab] = useState<Tab>(initialTab);
	const [search, setSearch] = useState('');
	const [collapsed, setCollapsed] = useState<Set<string>>(() => new Set());
	const [activeCategory, setActiveCategory] = useState<string>('people');
	// Saved-media chip state mirrors the desktop EmojiPicker so
	// filter chips land on the same 4 buckets.
	const [mediaFilter, setMediaFilter] = useState<
		'all' | 'image' | 'video' | 'audio'
	>('all');

	// Saved library lookup — same shape as the desktop picker uses.
	// `skip` when the Media tab is closed so we don't pay for the
	// query on first mount.
	const myUserId =
		typeof localStorage !== 'undefined' ? localStorage.getItem('userId') : null;
	const savedMedia =
		useQuery(
			api.savedMedia.list,
			myUserId && activeTab === 'media'
				? { userId: myUserId as any }
				: 'skip',
		) ?? [];

	const handleRepostSaved = useCallback(
		(item: any) => {
			// Same custom event the desktop picker dispatches — the
			// composer listens for it and feeds the attachment into
			// its own send path without re-uploading bytes.
			window.dispatchEvent(
				new CustomEvent('brycord:repost-saved-media', {
					detail: {
						url: item.url,
						filename: item.filename,
						mimeType: item.mimeType,
						width: item.width,
						height: item.height,
						size: item.size,
						encryptionKey: item.encryptionKey,
						encryptionIv: item.encryptionIv,
					},
				}),
			);
			onClose();
		},
		[onClose],
	);

	const searchRef = useRef<HTMLInputElement>(null);
	const tabBarRef = useRef<HTMLDivElement>(null);
	const bodyRef = useRef<HTMLDivElement>(null);
	const categoryRefs = useRef<Record<string, HTMLDivElement | null>>({});

	// Snap to the caller's preferred tab every time the sheet opens fresh.
	useEffect(() => {
		if (isOpen) {
			setActiveTab(initialTab);
			setSearch('');
			setMediaFilter('all');
		}
	}, [isOpen, initialTab]);

	// Also clear the search box when flipping between tabs so a
	// partial emoji search doesn't leak into the Media filename
	// filter (or vice versa).
	useEffect(() => {
		setSearch('');
	}, [activeTab]);

	// Focus the search input once the slide-in animation settles.
	useEffect(() => {
		if (!isOpen) return;
		const t = setTimeout(() => searchRef.current?.focus(), 260);
		return () => clearTimeout(t);
	}, [isOpen]);

	const allEmojis = useMemo(() => {
		const result: Record<string, EmojiEntry[]> = {};
		for (const cat of CATEGORIES) {
			const entries: EmojiEntry[] = (emojiData as any)[cat.key] || [];
			result[cat.key] = entries;
		}
		return result;
	}, []);

	const filtered = useMemo(() => {
		if (!search.trim()) return null;
		const q = search.toLowerCase();
		const unicodeResults: EmojiEntry[] = [];
		for (const cat of Object.values(allEmojis)) {
			for (const emoji of cat) {
				if (emoji.names.some((n) => n.includes(q))) {
					unicodeResults.push(emoji);
				}
			}
		}
		return { unicodeResults };
	}, [search, allEmojis]);

	const handleSelectUnicode = useCallback(
		(entry: EmojiEntry) => {
			onSelect({
				kind: 'unicode',
				surrogates: entry.surrogates,
				name: entry.names[0] ?? entry.surrogates,
			});
			onClose();
		},
		[onSelect, onClose],
	);

	const toggleCategory = (key: string) => {
		setCollapsed((prev) => {
			const next = new Set(prev);
			if (next.has(key)) next.delete(key);
			else next.add(key);
			return next;
		});
	};

	const scrollToCategory = (key: string) => {
		setCollapsed((prev) => {
			if (!prev.has(key)) return prev;
			const next = new Set(prev);
			next.delete(key);
			return next;
		});
		setActiveCategory(key);
		requestAnimationFrame(() => {
			const el = categoryRefs.current[key];
			const body = bodyRef.current;
			if (!el || !body) return;
			const bodyRect = body.getBoundingClientRect();
			const elRect = el.getBoundingClientRect();
			const target = body.scrollTop + (elRect.top - bodyRect.top);
			body.scrollTo({ top: target, behavior: 'smooth' });
		});
	};

	const handleBodyScroll = () => {
		if (search) return;
		const body = bodyRef.current;
		if (!body) return;
		const bodyTop = body.getBoundingClientRect().top;
		let current = activeCategory;
		for (const cat of CATEGORIES) {
			const el = categoryRefs.current[cat.key];
			if (!el) continue;
			const elTop = el.getBoundingClientRect().top - bodyTop;
			if (elTop <= 24) current = cat.key;
		}
		if (current !== activeCategory) setActiveCategory(current);
	};

	const activeIndex = TABS.findIndex((t) => t.id === activeTab);
	const tabBackgroundStyle: React.CSSProperties = {
		width: `calc((100% - 6px) / ${TABS.length})`,
		left: `calc(3px + (100% - 6px) * ${activeIndex} / ${TABS.length})`,
	};

	return (
		<BottomSheet isOpen={isOpen} onClose={onClose} disableDefaultHeader showHandle>
			<div className={styles.root}>
				{/* Segmented tab selector */}
				<div className={styles.tabBar} ref={tabBarRef} role="tablist">
					<div className={styles.tabBackground} style={tabBackgroundStyle} />
					{TABS.map((tab) => {
						const isActive = tab.id === activeTab;
						return (
							<button
								key={tab.id}
								type="button"
								role="tab"
								aria-selected={isActive}
								className={`${styles.tab} ${isActive ? styles.tabActive : ''} ${
									!tab.enabled ? styles.tabDisabled : ''
								}`}
								onClick={() => {
									if (tab.enabled) setActiveTab(tab.id);
								}}
							>
								{tab.label}
							</button>
						);
					})}
				</div>

				{/* GIFs tab has its own internal search bar + categories,
				    so we hide the sheet's generic search row to avoid a
				    double search surface. Stickers is still a stub
				    "Coming Soon" screen, so we hide the search there too. */}
				{activeTab !== 'gifs' && activeTab !== 'stickers' && (
					<div className={styles.searchBar}>
						<MagnifyingGlass
							size={18}
							weight="regular"
							className={styles.searchIcon}
						/>
						<input
							ref={searchRef}
							className={styles.searchInput}
							placeholder={
								activeTab === 'media'
									? 'Search saved media'
									: 'Find the emoji of your dreams'
							}
							value={search}
							onChange={(e) => setSearch(e.target.value)}
						/>
						{search && (
							<button
								type="button"
								className={styles.searchClear}
								onClick={() => setSearch('')}
								aria-label="Clear search"
							>
								<X size={12} weight="bold" />
							</button>
						)}
					</div>
				)}

				{/* Filter chips — Media tab only. Matches the desktop
				    picker's chip row exactly so the mobile surface feels
				    the same. */}
				{activeTab === 'media' && (
					<div className={styles.filterChips}>
						{(
							[
								{ id: 'all', label: 'All' },
								{ id: 'image', label: 'Images' },
								{ id: 'video', label: 'Videos' },
								{ id: 'audio', label: 'Audio' },
							] as const
						).map((chip) => {
							const isActive = mediaFilter === chip.id;
							return (
								<button
									key={chip.id}
									type="button"
									className={`${styles.filterChip} ${
										isActive ? styles.filterChipActive : ''
									}`}
									onClick={() => setMediaFilter(chip.id)}
								>
									{chip.label}
								</button>
							);
						})}
					</div>
				)}

				<div
					className={styles.body}
					ref={bodyRef}
					onScroll={handleBodyScroll}
				>
					{activeTab === 'gifs' ? (
						<GifPicker
							onSelectGif={(url) => {
								onSelect({ kind: 'gif', url });
								onClose();
							}}
						/>
					) : activeTab === 'media' ? (
						(() => {
							// Filter by chip + filename search, same logic
							// as the desktop picker.
							const q = search.trim().toLowerCase();
							const filteredSaved = (savedMedia as any[]).filter(
								(item) => {
									if (
										mediaFilter !== 'all' &&
										(item.kind ?? '') !== mediaFilter
									) {
										return false;
									}
									if (
										q &&
										!(item.filename ?? '').toLowerCase().includes(q)
									) {
										return false;
									}
									return true;
								},
							);
							if (savedMedia.length === 0) {
								return (
									<div className={styles.comingSoon}>
										Nothing saved yet. Star an attachment in chat to
										bookmark it here for quick re-sharing.
									</div>
								);
							}
							if (filteredSaved.length === 0) {
								return (
									<div className={styles.comingSoon}>
										No saved media match your filter.
									</div>
								);
							}
							return (
								<div className={styles.mediaGrid}>
									{filteredSaved.map((item: any) => {
										const isImage = item.kind === 'image';
										const isVideo = item.kind === 'video';
										return (
											<button
												key={item._id}
												type="button"
												className={styles.mediaCard}
												title={item.filename}
												onClick={() => handleRepostSaved(item)}
											>
												{isImage ? (
													<ImageSquare
														size={28}
														weight="fill"
														className={styles.mediaCardIcon}
													/>
												) : isVideo ? (
													<Gif
														size={28}
														weight="fill"
														className={styles.mediaCardIcon}
													/>
												) : (
													<Sticker
														size={28}
														weight="fill"
														className={styles.mediaCardIcon}
													/>
												)}
												<span className={styles.mediaCardName}>
													{item.filename}
												</span>
											</button>
										);
									})}
								</div>
							);
						})()
					) : activeTab === 'stickers' ? (
						<div className={styles.comingSoon}>Coming Soon</div>
					) : filtered ? (
						<div className={styles.searchResults}>
							<div className={styles.searchResultsLabel}>Results</div>
							{filtered.unicodeResults.length === 0 ? (
								<div className={styles.noResults}>No emoji found</div>
							) : (
								<div className={styles.categoryGrid}>
									{filtered.unicodeResults.map((emoji, idx) => (
										<button
											key={`${emoji.surrogates}-${idx}`}
											type="button"
											className={styles.emojiButton}
											onClick={() => handleSelectUnicode(emoji)}
										>
											<TwemojiTile surrogates={emoji.surrogates} />
										</button>
									))}
								</div>
							)}
						</div>
					) : (
						<>
							{CATEGORIES.map((cat) => (
								<CategorySection
									key={cat.key}
									title={cat.label}
									isCollapsed={collapsed.has(cat.key)}
									onToggle={() => toggleCategory(cat.key)}
									sectionRef={(el) => {
										categoryRefs.current[cat.key] = el;
									}}
								>
									{allEmojis[cat.key].map((emoji, idx) => (
										<button
											key={`${cat.key}-${emoji.surrogates}-${idx}`}
											type="button"
											className={styles.emojiButton}
											onClick={() => handleSelectUnicode(emoji)}
										>
											<TwemojiTile surrogates={emoji.surrogates} />
										</button>
									))}
								</CategorySection>
							))}
						</>
					)}
				</div>

				{activeTab === 'emojis' && !search && (
					<div className={styles.bottomCategoryBar}>
						{CATEGORIES.map((cat) => {
							const Icon = cat.icon;
							return (
								<button
									key={cat.key}
									type="button"
									className={`${styles.bottomCategoryButton} ${
										activeCategory === cat.key
											? styles.bottomCategoryButtonActive
											: ''
									}`}
									onClick={() => scrollToCategory(cat.key)}
									aria-label={cat.label}
								>
									<Icon size={20} weight="fill" />
								</button>
							);
						})}
					</div>
				)}
			</div>

			{/* Silence the unused import warning — emojiToCodepoint is
			    referenced elsewhere in TwemojiTile variants and kept for
			    parity with the desktop picker's helpers. */}
			<span style={{ display: 'none' }}>{emojiToCodepoint('')}</span>
		</BottomSheet>
	);
}

interface CategorySectionProps {
	title: string;
	isCollapsed: boolean;
	onToggle: () => void;
	children: React.ReactNode;
	sectionRef?: (el: HTMLDivElement | null) => void;
}

function CategorySection({
	title,
	isCollapsed,
	onToggle,
	children,
	sectionRef,
}: CategorySectionProps) {
	return (
		<div className={styles.categorySection} ref={sectionRef}>
			<button
				type="button"
				className={styles.categoryHeader}
				onClick={onToggle}
				aria-expanded={!isCollapsed}
			>
				<CaretRight
					size={14}
					weight="bold"
					className={`${styles.categoryCaret} ${
						!isCollapsed ? styles.categoryCaretOpen : ''
					}`}
				/>
				<span>{title}</span>
			</button>
			{!isCollapsed && <div className={styles.categoryGrid}>{children}</div>}
		</div>
	);
}
