<script lang="ts">
	import { browser } from '$app/environment';
	import { goto } from '$app/navigation';
	import { cn } from '$lib/utils';

	interface Props {
		onclose: () => void;
	}
	let { onclose }: Props = $props();

	// ── Types ─────────────────────────────────────────────────────────────────
	interface SearchResult {
		slug: string;
		title: string;
		cover?: string;
		author?: string;
		genres?: string[];
		status?: string;
		chapters?: string;  // e.g. "42 chapters"
		url?: string;       // novelfire source url — present for remote results
	}

	interface SearchResponse {
		results: SearchResult[];
		local_count: number;
		remote_count: number;
	}

	// ── State ─────────────────────────────────────────────────────────────────
	const RECENTS_KEY = 'search_recents_v1';
	const MAX_RECENTS = 8;

	function loadRecents(): string[] {
		if (!browser) return [];
		try {
			const raw = localStorage.getItem(RECENTS_KEY);
			if (raw) return JSON.parse(raw) as string[];
		} catch { /* ignore */ }
		return [];
	}

	function saveRecents(list: string[]) {
		if (!browser) return;
		try { localStorage.setItem(RECENTS_KEY, JSON.stringify(list)); } catch { /* ignore */ }
	}

	let recents = $state<string[]>(loadRecents());
	let query   = $state('');
	let results = $state<SearchResult[]>([]);
	let localCount  = $state(0);
	let remoteCount = $state(0);
	let loading = $state(false);
	let error   = $state('');

	// For keyboard navigation through results
	let selectedIdx = $state(-1);

	// Input element ref for autofocus
	let inputEl = $state<HTMLInputElement | null>(null);

	// ── Autofocus + body scroll lock ──────────────────────────────────────────
	$effect(() => {
		if (inputEl) inputEl.focus();
		if (browser) {
			const prev = document.body.style.overflow;
			document.body.style.overflow = 'hidden';
			return () => { document.body.style.overflow = prev; };
		}
	});

	// ── Keyboard shortcuts (global): Escape closes ────────────────────────────
	function onKeydown(e: KeyboardEvent) {
		if (e.key === 'Escape') { onclose(); return; }

		const total = visibleResults.length;
		if (total === 0) return;

		if (e.key === 'ArrowDown') {
			e.preventDefault();
			selectedIdx = (selectedIdx + 1) % total;
		} else if (e.key === 'ArrowUp') {
			e.preventDefault();
			selectedIdx = (selectedIdx - 1 + total) % total;
		} else if (e.key === 'Enter' && selectedIdx >= 0) {
			e.preventDefault();
			navigateTo(visibleResults[selectedIdx]);
		}
	}

	// ── Debounced search ──────────────────────────────────────────────────────
	let debounceTimer = 0;

	$effect(() => {
		const q = query.trim();
		selectedIdx = -1;

		if (q.length < 2) {
			results = [];
			localCount = 0;
			remoteCount = 0;
			loading = false;
			error = '';
			clearTimeout(debounceTimer);
			return;
		}

		loading = true;
		error = '';
		clearTimeout(debounceTimer);
		debounceTimer = setTimeout(async () => {
			try {
				const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`);
				if (!res.ok) throw new Error(`HTTP ${res.status}`);
				const data: SearchResponse = await res.json();
				results    = data.results ?? [];
				localCount  = data.local_count ?? 0;
				remoteCount = data.remote_count ?? 0;
			} catch (e) {
				error = 'Search failed. Please try again.';
				results = [];
			} finally {
				loading = false;
			}
		}, 300) as unknown as number;
	});

	// Results visible in the list — same as results (no client-side filter needed)
	const visibleResults = $derived(results);

	// ── Genre suggestions shown when query is empty ───────────────────────────
	const GENRE_SUGGESTIONS = [
		'Fantasy', 'Action', 'Romance', 'Cultivation', 'System',
		'Reincarnation', 'Sci-Fi', 'Horror', 'Slice of Life', 'Adventure',
	];

	// ── Navigation helpers ────────────────────────────────────────────────────
	function navigateTo(r: SearchResult) {
		pushRecent(query.trim());
		goto(`/books/${r.slug}`);
		onclose();
	}

	function searchGenre(genre: string) {
		goto(`/catalogue?genre=${encodeURIComponent(genre)}`);
		onclose();
	}

	function submitQuery() {
		const q = query.trim();
		if (!q) return;
		pushRecent(q);
		goto(`/catalogue?q=${encodeURIComponent(q)}`);
		onclose();
	}

	// ── Recent searches ───────────────────────────────────────────────────────
	function pushRecent(q: string) {
		if (!q || q.length < 2) return;
		const next = [q, ...recents.filter(r => r.toLowerCase() !== q.toLowerCase())].slice(0, MAX_RECENTS);
		recents = next;
		saveRecents(next);
	}

	function removeRecent(q: string) {
		const next = recents.filter(r => r !== q);
		recents = next;
		saveRecents(next);
	}

	function clearAllRecents() {
		recents = [];
		saveRecents([]);
	}

	function applyRecent(q: string) {
		query = q;
		if (inputEl) inputEl.focus();
	}

	// ── Helpers ───────────────────────────────────────────────────────────────
	function parseGenres(genres: string[] | string | undefined): string[] {
		if (!genres) return [];
		if (Array.isArray(genres)) return genres;
		try { const p = JSON.parse(genres); return Array.isArray(p) ? p : []; } catch { return []; }
	}

	const isRemote = (r: SearchResult) => r.url != null && r.url.includes('novelfire');
</script>

<!-- svelte:window for global keyboard handling -->
<svelte:window onkeydown={onKeydown} />

<!-- Backdrop -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
	class="fixed inset-0 z-[70] flex flex-col"
	style="background: rgba(0,0,0,0.6); backdrop-filter: blur(4px);"
	onpointerdown={(e) => { if (e.target === e.currentTarget) onclose(); }}
>
	<!-- Modal panel — slides down from top -->
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div
		class="w-full max-w-2xl mx-auto mt-0 sm:mt-16 flex flex-col bg-(--color-surface) sm:rounded-2xl border-b sm:border border-(--color-border) shadow-2xl overflow-hidden"
		style="max-height: 100svh; sm:max-height: calc(100svh - 8rem);"
		onpointerdown={(e) => e.stopPropagation()}
	>
		<!-- ── Search input row ──────────────────────────────────────────────── -->
		<div class="flex items-center gap-3 px-4 py-3 border-b border-(--color-border) shrink-0">
			<!-- Search icon -->
			{#if loading}
				<svg class="w-5 h-5 text-(--color-brand) animate-spin shrink-0" fill="none" viewBox="0 0 24 24">
					<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
					<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
				</svg>
			{:else}
				<svg class="w-5 h-5 text-(--color-muted) shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
				</svg>
			{/if}

			<input
				bind:this={inputEl}
				bind:value={query}
				type="search"
				placeholder="Search books, authors, genres…"
				class="flex-1 bg-transparent text-(--color-text) placeholder:text-(--color-muted) text-base focus:outline-none min-w-0"
				onkeydown={(e) => { if (e.key === 'Enter') { e.preventDefault(); submitQuery(); } }}
				autocomplete="off"
				autocorrect="off"
				spellcheck={false}
			/>

			{#if query}
				<button
					type="button"
					onclick={() => { query = ''; inputEl?.focus(); }}
					class="shrink-0 p-1 rounded-full text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
					aria-label="Clear search"
				>
					<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
					</svg>
				</button>
			{/if}

			<button
				type="button"
				onclick={onclose}
				class="shrink-0 px-3 py-1 rounded-lg text-sm text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
				aria-label="Close search"
			>
				Cancel
			</button>
		</div>

		<!-- ── Scrollable body ───────────────────────────────────────────────── -->
		<div class="flex-1 overflow-y-auto overscroll-contain">

			<!-- ── Error state ─────────────────────────────────────────────── -->
			{#if error}
				<p class="px-5 py-8 text-sm text-center text-(--color-danger)">{error}</p>

			<!-- ── Results ─────────────────────────────────────────────────── -->
			{:else if visibleResults.length > 0}
				<!-- Result count + "see all" hint -->
				<div class="flex items-center justify-between px-4 pt-3 pb-1.5">
					<p class="text-xs text-(--color-muted)">
						{#if localCount > 0 && remoteCount > 0}
							<span class="text-(--color-text) font-medium">{localCount}</span> in library
							· <span class="text-(--color-text) font-medium">{remoteCount}</span> from Novelfire
						{:else if localCount > 0}
							<span class="text-(--color-text) font-medium">{localCount}</span> in library
						{:else}
							<span class="text-(--color-text) font-medium">{remoteCount}</span> from Novelfire
						{/if}
					</p>
					<!-- "All results in catalogue" shortcut -->
					<button
						type="button"
						onclick={submitQuery}
						class="text-xs text-(--color-brand) hover:text-(--color-brand-dim) transition-colors"
					>
						See all in catalogue →
					</button>
				</div>

				{#each visibleResults as r, i}
					{@const genres = parseGenres(r.genres)}
					{@const remote = isRemote(r)}
					<button
						type="button"
						onclick={() => navigateTo(r)}
						class={cn(
							'w-full flex items-center gap-3 px-4 py-3 text-left transition-colors border-b border-(--color-border)/40 last:border-0',
							selectedIdx === i ? 'bg-(--color-surface-2)' : 'hover:bg-(--color-surface-2)'
						)}
					>
						<!-- Cover thumbnail -->
						<div class="shrink-0 w-10 h-14 rounded overflow-hidden bg-(--color-surface-2) border border-(--color-border)">
							{#if r.cover}
								<img src={r.cover} alt="" class="w-full h-full object-cover" loading="lazy" />
							{:else}
								<div class="w-full h-full flex items-center justify-center">
									<svg class="w-5 h-5 text-(--color-muted)/40" fill="currentColor" viewBox="0 0 24 24">
										<path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/>
									</svg>
								</div>
							{/if}
						</div>

						<!-- Info -->
						<div class="flex-1 min-w-0">
							<div class="flex items-start gap-2">
								<p class="text-sm font-semibold text-(--color-text) leading-snug line-clamp-1 flex-1">
									{r.title}
								</p>
								{#if remote}
									<span class="shrink-0 text-[10px] font-semibold px-1.5 py-0.5 rounded bg-(--color-surface-3) text-(--color-muted) border border-(--color-border) leading-none mt-0.5">
										Novelfire
									</span>
								{/if}
							</div>
							{#if r.author}
								<p class="text-xs text-(--color-muted) mt-0.5 truncate">{r.author}</p>
							{/if}
							<div class="flex items-center gap-1.5 mt-1 flex-wrap">
								{#if r.chapters}
									<span class="text-xs text-(--color-muted)/60">{r.chapters}</span>
								{/if}
								{#if r.status}
									<span class="text-xs text-(--color-muted)/60 capitalize">{r.status}</span>
								{/if}
								{#each genres.slice(0, 2) as g}
									<span class="text-[10px] px-1.5 py-0.5 rounded-full bg-(--color-surface-3) text-(--color-muted)">{g}</span>
								{/each}
							</div>
						</div>

						<!-- Chevron -->
						<svg class="w-4 h-4 text-(--color-muted)/40 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
							<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
						</svg>
					</button>
				{/each}

				<!-- "See all results" footer button -->
				<button
					type="button"
					onclick={submitQuery}
					class="w-full flex items-center justify-center gap-2 px-4 py-4 text-sm text-(--color-brand) hover:bg-(--color-surface-2) transition-colors"
				>
					<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
					</svg>
					See all results for "{query.trim()}"
				</button>

			<!-- ── No results ───────────────────────────────────────────────── -->
			{:else if query.trim().length >= 2 && !loading}
				<div class="px-5 py-10 text-center">
					<p class="text-sm font-semibold text-(--color-text) mb-1">No results for "{query.trim()}"</p>
					<p class="text-xs text-(--color-muted) mb-5">Try a different title, author, or browse by genre below.</p>
					<div class="flex flex-wrap gap-2 justify-center">
						{#each GENRE_SUGGESTIONS as genre}
							<button
								type="button"
								onclick={() => searchGenre(genre)}
								class="px-3 py-1.5 rounded-full border border-(--color-border) bg-(--color-surface-2) text-sm text-(--color-muted) hover:text-(--color-text) hover:border-(--color-brand)/50 transition-colors"
							>
								{genre}
							</button>
						{/each}
					</div>
				</div>

			<!-- ── Empty state (query too short or empty) ───────────────────── -->
			{:else if query.trim().length === 0}
				<!-- Recent searches -->
				{#if recents.length > 0}
					<div class="px-4 pt-4 pb-2">
						<div class="flex items-center justify-between mb-2">
							<p class="text-xs font-semibold text-(--color-muted) uppercase tracking-wider">Recent</p>
							<button
								type="button"
								onclick={clearAllRecents}
								class="text-xs text-(--color-muted) hover:text-(--color-text) transition-colors"
							>
								Clear all
							</button>
						</div>
						{#each recents as r}
							<div class="flex items-center gap-2 group">
								<button
									type="button"
									onclick={() => applyRecent(r)}
									class="flex-1 flex items-center gap-2.5 px-1 py-2 rounded-lg text-sm text-(--color-text) hover:bg-(--color-surface-2) transition-colors text-left"
								>
									<svg class="w-3.5 h-3.5 text-(--color-muted)/50 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
										<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
									</svg>
									{r}
								</button>
								<button
									type="button"
									onclick={() => removeRecent(r)}
									class="shrink-0 p-1 rounded text-(--color-muted)/40 hover:text-(--color-muted) opacity-0 group-hover:opacity-100 transition-all"
									aria-label="Remove"
								>
									<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
										<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
									</svg>
								</button>
							</div>
						{/each}
					</div>
					<div class="mx-4 my-2 border-t border-(--color-border)/60"></div>
				{/if}

				<!-- Genre suggestions -->
				<div class="px-4 pt-3 pb-5">
					<p class="text-xs font-semibold text-(--color-muted) uppercase tracking-wider mb-3">Browse by genre</p>
					<div class="flex flex-wrap gap-2">
						{#each GENRE_SUGGESTIONS as genre}
							<button
								type="button"
								onclick={() => searchGenre(genre)}
								class="px-3 py-1.5 rounded-full border border-(--color-border) bg-(--color-surface-2) text-sm text-(--color-muted) hover:text-(--color-text) hover:border-(--color-brand)/50 transition-colors"
							>
								{genre}
							</button>
						{/each}
					</div>
				</div>
			{/if}
		</div>
	</div>
</div>
