/**
 * SearchDrawer — mobile bottom sheet opened by tapping the search icon
 * in ChannelHeader on mobile. Wraps SearchStore in a BottomSheet with a
 * search input, filter chips, and a compact mobile-styled result list.
 */
import { observer } from 'mobx-react-lite';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { MagnifyingGlass, X, Hash } from '@phosphor-icons/react';
import { Avatar, BottomSheet } from '@brycord/ui';
import SearchStore, { type SearchResult } from '@app/stores/SearchStore';
import ChannelStore from '@app/stores/ChannelStore';
import { MessageContent } from './MessageContent';
import { LinkEmbeds } from './LinkEmbed';
import styles from './SearchDrawer.module.css';

interface SearchDrawerProps {
	isOpen: boolean;
	onClose: () => void;
	serverId?: string;
	onScrollToMessage?: (messageId: string) => void;
}

const FILTER_CHIPS: Array<{ key: string; label: string }> = [
	{ key: 'from:', label: 'From' },
	{ key: 'has:', label: 'Has' },
	{ key: 'mentions:', label: 'Mentions' },
	{ key: 'before:', label: 'Before' },
	{ key: 'after:', label: 'After' },
	{ key: 'pinned:true', label: 'Pinned' },
];

function formatTimestamp(ts: number): string {
	const date = new Date(ts);
	const now = new Date();
	const isToday = date.toDateString() === now.toDateString();
	const yesterday = new Date(now);
	yesterday.setDate(yesterday.getDate() - 1);
	const isYesterday = date.toDateString() === yesterday.toDateString();
	const time = date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });
	if (isToday) return `Today at ${time}`;
	if (isYesterday) return `Yesterday at ${time}`;
	return `${date.toLocaleDateString()} ${time}`;
}

function groupByChannel(
	results: SearchResult[],
): Array<{ channelId: string; results: SearchResult[] }> {
	const groups = new Map<string, SearchResult[]>();
	for (const result of results) {
		const cId = result.message.channelId;
		if (!groups.has(cId)) groups.set(cId, []);
		groups.get(cId)!.push(result);
	}
	return Array.from(groups.entries()).map(([channelId, results]) => ({ channelId, results }));
}

/**
 * Compact mobile search result list — renders a "N Results" counter, then
 * channel-grouped cards. Each card shows the avatar, author, timestamp,
 * and a preview of the message content. Tapping a card jumps to the
 * message in the underlying chat view.
 */
const MobileSearchResults = observer(function MobileSearchResults({
	onJump,
}: {
	onJump: (messageId: string) => void;
}) {
	const { query, results, isSearching } = SearchStore;
	const hasQuery = query.trim().length > 0;
	const grouped = useMemo(() => groupByChannel(results), [results]);

	if (!hasQuery) {
		return (
			<div className={styles.placeholder}>
				<MagnifyingGlass size={48} weight="regular" />
				<div className={styles.placeholderTitle}>Search Messages</div>
				<div className={styles.placeholderSubtitle}>
					Use filters or enter keywords to find messages
				</div>
			</div>
		);
	}

	if (isSearching) {
		return (
			<div className={styles.placeholder}>
				<div className={styles.placeholderSubtitle}>Searching…</div>
			</div>
		);
	}

	if (results.length === 0) {
		return (
			<div className={styles.placeholder}>
				<div className={styles.placeholderTitle}>No results</div>
				<div className={styles.placeholderSubtitle}>Try a different search term.</div>
			</div>
		);
	}

	return (
		<div className={styles.resultsList}>
			<div className={styles.resultsCount}>
				{results.length} {results.length === 1 ? 'Result' : 'Results'}
			</div>
			{grouped.map((group) => {
				const channel = ChannelStore.getChannel(group.channelId);
				return (
					<div key={group.channelId} className={styles.resultsGroup}>
						<div className={styles.resultsGroupHeader}>
							<Hash size={14} weight="bold" />
							<span>{channel?.name || 'Unknown'}</span>
						</div>
						<div className={styles.resultsGroupBody}>
							{group.results.map((result) => {
								const { message } = result;
								const author = message.author;
								return (
									<div
										key={message.id}
										role="button"
										tabIndex={0}
										className={styles.resultCard}
										onClick={() => onJump(message.id)}
										onKeyDown={(e) => {
											if (e.key === 'Enter' || e.key === ' ') {
												e.preventDefault();
												onJump(message.id);
											}
										}}
									>
										<div className={styles.resultCardAvatar}>
											<Avatar
												src={author.avatar}
												fallback={author.displayName || author.username}
												size={32}
											/>
										</div>
										<div className={styles.resultCardBody}>
											<div className={styles.resultCardHeader}>
												<span className={styles.resultCardAuthor}>
													{author.displayName || author.username}
												</span>
												<span className={styles.resultCardTimestamp}>
													{formatTimestamp(message.timestamp)}
												</span>
											</div>
											{message.content && (
												<div className={styles.resultCardContent}>
													<MessageContent content={message.content} />
												</div>
											)}
											{!message.decryptionFailed && (
												<div
													className={styles.resultCardEmbeds}
													onClick={(e) => e.stopPropagation()}
												>
													<LinkEmbeds content={message.content} />
												</div>
											)}
											{message.attachments.length > 0 && (
												<div className={styles.resultCardAttachments}>
													{message.attachments.map((att) =>
														att.contentType.startsWith('image/') ? (
															<img
																key={att.id}
																src={att.url}
																alt={att.filename}
																className={styles.resultCardImage}
																loading="lazy"
															/>
														) : (
															<a
																key={att.id}
																href={att.url}
																className={styles.resultCardFile}
																target="_blank"
																rel="noopener noreferrer"
																onClick={(e) => e.stopPropagation()}
															>
																{att.filename}
															</a>
														),
													)}
												</div>
											)}
										</div>
									</div>
								);
							})}
						</div>
					</div>
				);
			})}
		</div>
	);
});

export const SearchDrawer = observer(function SearchDrawer({
	isOpen,
	onClose,
	serverId,
	onScrollToMessage,
}: SearchDrawerProps) {
	const inputRef = useRef<HTMLInputElement>(null);
	const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);

	// Focus the input whenever the drawer opens — matches Fluxer's UX
	// where the keyboard appears immediately on open.
	useEffect(() => {
		if (!isOpen) return;
		const t = setTimeout(() => inputRef.current?.focus(), 120);
		return () => clearTimeout(t);
	}, [isOpen]);

	useEffect(() => {
		return () => {
			if (debounceRef.current) clearTimeout(debounceRef.current);
		};
	}, []);

	const handleChange = useCallback(
		(e: React.ChangeEvent<HTMLInputElement>) => {
			const value = e.target.value;
			SearchStore.setQuery(value);
			if (debounceRef.current) clearTimeout(debounceRef.current);
			if (value.trim() && serverId) {
				if (!SearchStore.isOpen) SearchStore.openSearch();
				debounceRef.current = setTimeout(() => {
					SearchStore.searchMessages(serverId, value);
				}, 300);
			} else if (SearchStore.isOpen) {
				SearchStore.closeSearch();
			}
		},
		[serverId],
	);

	const handleKeyDown = useCallback(
		(e: React.KeyboardEvent) => {
			if (e.key === 'Enter' && SearchStore.query.trim() && serverId) {
				if (!SearchStore.isOpen) SearchStore.openSearch();
				SearchStore.searchMessages(serverId, SearchStore.query);
			}
		},
		[serverId],
	);

	const handleChipClick = useCallback((key: string) => {
		const current = SearchStore.query;
		const separator = current.length > 0 && !current.endsWith(' ') ? ' ' : '';
		SearchStore.setQuery(current + separator + key + ' ');
		inputRef.current?.focus();
	}, []);

	const handleClear = useCallback(() => {
		SearchStore.setQuery('');
		SearchStore.closeSearch();
		inputRef.current?.focus();
	}, []);

	const handleSearch = useCallback(() => {
		if (!serverId || !SearchStore.query.trim()) return;
		if (!SearchStore.isOpen) SearchStore.openSearch();
		SearchStore.searchMessages(serverId, SearchStore.query);
	}, [serverId]);

	return (
		<BottomSheet isOpen={isOpen} onClose={onClose} title="Search">
			<div className={styles.body}>
				<div className={styles.searchInputWrapper}>
					<MagnifyingGlass size={16} className={styles.searchInputIcon} />
					<input
						ref={inputRef}
						className={styles.searchInput}
						placeholder="Search messages"
						value={SearchStore.query}
						onChange={handleChange}
						onKeyDown={handleKeyDown}
					/>
					{SearchStore.query && (
						<button
							type="button"
							className={styles.searchClearButton}
							onClick={handleClear}
							aria-label="Clear search"
						>
							<X size={14} weight="bold" />
						</button>
					)}
				</div>

				<div className={styles.chipRow}>
					{FILTER_CHIPS.map((chip) => (
						<button
							key={chip.key}
							type="button"
							className={styles.chip}
							onClick={() => handleChipClick(chip.key)}
						>
							{chip.label}
						</button>
					))}
				</div>

				<button
					type="button"
					className={styles.searchButton}
					onClick={handleSearch}
					disabled={!SearchStore.query.trim() || !serverId}
				>
					Search
				</button>

				<div className={styles.results}>
					<MobileSearchResults
						onJump={(id) => {
							onScrollToMessage?.(id);
							onClose();
						}}
					/>
				</div>
			</div>
		</BottomSheet>
	);
});
