<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import DownloadStatusDock, { type DockItem } from '$lib/components/media/DownloadStatusDock.svelte';

	interface Release {
		title: string; content_type: string; format: string; size: string;
		source: string; source_id: string; info_url: string; language: string;
		extra: { author?: string; preview?: string; publisher?: string; year?: string };
	}
	interface Download {
		id: string; title: string; author: string; format: string; source: string;
		status: string; status_message: string; progress: number; download_path: string;
		content_type: string; added_time: string;
	}
	interface Library { id: number; name: string; paths: { id: number; path: string }[]; }

	let query = $state('');
	let results = $state<Release[]>([]);
	let bookMeta = $state<{ title: string; authors: string[]; cover_url: string | null } | null>(null);
	let searching = $state(false);
	let searched = $state(false);
	let downloads = $state<Record<string, Download>>({});
	let libraries = $state<Library[]>([]);
	let defaultLibraryId = $state<number | null>(null);
	let downloadingIds = $state<Set<string>>(new Set());
	let importingIds = $state<Set<string>>(new Set());
	let importedIds = $state<Set<string>>(new Set());
	let perBookLibrary = $state<Record<string, number>>({});
	let prevCompleted = $state<Set<string>>(new Set());
	let poll: ReturnType<typeof setInterval> | null = null;
	let statusEvents = $state<DockItem[]>([]);

	// Kindle auto-send
	interface KindleTarget { id: string; label: string; email: string; }
	let kindleTargets = $state<KindleTarget[]>([]);
	let kindleConfigured = $state(false);
	let autoKindleTarget = $state<string>('none');
	let kindleSending = $state<Set<string>>(new Set());
	let kindleSent = $state<Set<string>>(new Set());

	const dockItems = $derived.by(() => {
		const activeItems: DockItem[] = [];
		for (const dl of Object.values(downloads)) {
			if (['queued', 'downloading', 'locating', 'resolving'].includes(dl.status)) {
				activeItems.push({
					id: `book-live-${dl.id}`,
					label: dl.title || 'Book download',
					message: dl.status_message || dl.status,
					tone: 'progress',
					progress: dl.progress > 0 ? dl.progress : null,
					meta: [dl.author, dl.format?.toUpperCase()].filter(Boolean).join(' · '),
					actionLabel: 'Cancel',
					actionKind: 'cancel'
				});
			}
		}

		for (const id of importingIds) {
			const dl = downloads[id];
			activeItems.push({
				id: `book-import-${id}`,
				label: dl?.title || 'Library import',
				message: 'Sending into your library',
				tone: 'progress',
				meta: dl?.author || 'Booklore import'
			});
		}

		for (const id of kindleSending) {
			const dl = downloads[id];
			activeItems.push({
				id: `book-kindle-${id}`,
				label: dl?.title || 'Kindle delivery',
				message: 'Sending to Kindle',
				tone: 'progress',
				meta: dl?.author || 'Kindle'
			});
		}

		return [...activeItems, ...statusEvents].slice(0, 4);
	});

	function pushStatusEvent(item: DockItem, ttl = 5200) {
		statusEvents = [item, ...statusEvents.filter((entry) => entry.id !== item.id)].slice(0, 6);
		const timer = setTimeout(() => {
			statusEvents = statusEvents.filter((entry) => entry.id !== item.id);
		}, ttl);
		return () => clearTimeout(timer);
	}

	async function search() {
		if (!query.trim()) return;
		searching = true; searched = true; results = []; bookMeta = null;
		try {
			const res = await fetch(`/api/books/releases?source=direct_download&query=${encodeURIComponent(query.trim())}&limit=40`, { credentials: 'include' });
			if (res.ok) { const data = await res.json(); results = data.releases || []; bookMeta = data.book || null; }
		} catch { /* silent */ }
		searching = false;
	}

	async function download(release: Release) {
		downloadingIds = new Set([...downloadingIds, release.source_id]);
		pushStatusEvent({
			id: `book-queued-${release.source_id}`,
			label: release.title,
			message: 'Added to the queue',
			tone: 'progress',
			meta: [release.extra?.author, release.format?.toUpperCase()].filter(Boolean).join(' · ')
		});
		try {
			const res = await fetch('/api/books/releases/download', {
				method: 'POST', credentials: 'include',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({ source: release.source, source_id: release.source_id, title: release.title, format: release.format, size: release.size, extra: release.extra })
			});
			if (res.ok) fetchStatus();
		} catch { /* silent */ }
	}

	async function cancelDownload(id: string) {
		try { await fetch(`/api/books/download/${id}/cancel`, { method: 'DELETE', credentials: 'include' }); fetchStatus(); } catch { /* silent */ }
	}

	async function retryDownload(id: string) {
		try { await fetch(`/api/books/download/${id}/retry`, { method: 'POST', credentials: 'include' }); fetchStatus(); } catch { /* silent */ }
	}

	function getLibraryId(key: string): number | null {
		return perBookLibrary[key] ?? defaultLibraryId;
	}

	async function importToBooklore(dl: Download, libraryId?: number | null) {
		const libId = libraryId ?? defaultLibraryId;
		if (!libId || importingIds.has(dl.id) || importedIds.has(dl.id)) return;
		const lib = libraries.find(l => l.id === libId);
		if (!lib || lib.paths.length === 0) return;
		importingIds = new Set([...importingIds, dl.id]);
		const fileName = dl.download_path?.split('/').pop() || dl.title;
		try {
			const res = await fetch('/api/booklore/import', {
				method: 'POST', credentials: 'include',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({ fileName, libraryId: lib.id, pathId: lib.paths[0].id })
			});
			if (res.ok) {
				importedIds = new Set([...importedIds, dl.id]);
				pushStatusEvent({
					id: `book-imported-${dl.id}`,
					label: dl.title || fileName,
					message: 'Imported into your library',
					tone: 'success',
					meta: lib.name
				});
				// Auto-send to Kindle if configured
				if (autoKindleTarget !== 'none' && fileName && !kindleSent.has(dl.id)) {
					sendFileToKindle(dl.id, fileName, dl.title || fileName, autoKindleTarget);
				}
			}
		} catch { /* silent */ }
		const next = new Set(importingIds); next.delete(dl.id); importingIds = next;
	}

	async function sendFileToKindle(dlId: string, filename: string, title: string, target: string) {
		if (kindleSending.has(dlId) || kindleSent.has(dlId)) return;
		kindleSending = new Set([...kindleSending, dlId]);
		try {
			const res = await fetch('/api/kindle/send-file', {
				method: 'POST', credentials: 'include',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({ filename, title, target }),
			});
			if (res.ok) {
				kindleSent = new Set([...kindleSent, dlId]);
				pushStatusEvent({
					id: `book-kindle-sent-${dlId}`,
					label: title,
					message: 'Sent to Kindle',
					tone: 'success',
					meta: 'Kindle delivery'
				});
			}
		} catch { /* silent */ }
		const next = new Set(kindleSending); next.delete(dlId); kindleSending = next;
	}

	async function fetchStatus() {
		try {
			const res = await fetch('/api/books/status', { credentials: 'include' });
			if (res.ok) {
				const previous = downloads;
				const data = await res.json();
				const all: Record<string, Download> = {};
				for (const [groupName, group] of Object.entries(data) as [string, Record<string, Download>][]) {
					if (group && typeof group === 'object') {
						for (const [id, dl] of Object.entries(group)) {
							if (['complete', 'error', 'cancelled'].includes(groupName)) dl.status = groupName;
							else if (groupName === 'active') dl.status = dl.status_message?.toLowerCase().includes('download') ? 'downloading' : (dl.status || 'downloading');
							all[id] = dl;
						}
					}
				}
				if (defaultLibraryId) {
					for (const [id, dl] of Object.entries(all)) {
						if (dl.status === 'complete' && !prevCompleted.has(id) && !importingIds.has(id) && !importedIds.has(id)) {
							importToBooklore(dl, getLibraryId(id));
						}
					}
				}
				prevCompleted = new Set(Object.entries(all).filter(([_, d]) => d.status === 'complete').map(([id]) => id));
				for (const [id, dl] of Object.entries(all)) {
					const prev = previous[id];
					if (prev && prev.status !== dl.status) {
						if (dl.status === 'complete') {
							pushStatusEvent({
								id: `book-complete-${id}`,
								label: dl.title || id,
								message: 'Download finished',
								tone: 'success',
								meta: [dl.author, dl.format?.toUpperCase()].filter(Boolean).join(' · ')
							});
						}
						if (dl.status === 'error') {
							pushStatusEvent({
								id: `book-error-${id}`,
								label: dl.title || id,
								message: dl.status_message || 'Download failed',
								tone: 'error',
								meta: dl.author || 'Book download',
								actionLabel: 'Retry',
								actionKind: 'retry'
							}, 8000);
						}
					}
				}
				downloads = all;
			}
		} catch { /* silent */ }
	}

	function handleDockAction(event: CustomEvent<{ id: string; actionKind: 'cancel' | 'retry' | 'remove' }>) {
		const { id, actionKind } = event.detail;
		const sourceId = id.replace(/^book-(live|import|kindle|complete|error|queued|imported|kindle-sent)-/, '');
		if (actionKind === 'cancel') {
			cancelDownload(sourceId);
		} else if (actionKind === 'retry') {
			retryDownload(sourceId);
		} else {
			cancelDownload(sourceId);
		}
	}

	async function fetchLibraries() {
		try {
			const res = await fetch('/api/booklore/libraries', { credentials: 'include' });
			if (res.ok) {
				const data = await res.json();
				libraries = data.libraries || [];
				if (libraries.length > 0 && defaultLibraryId === null) defaultLibraryId = libraries[0].id;
			}
		} catch { /* silent */ }
	}

	function coverUrl(release: Release): string | null {
		const preview = release.extra?.preview;
		if (!preview) return null;
		if (preview.startsWith('/api/')) {
			return `/api/books${preview.slice(4)}`;
		}
		return preview;
	}

	function fmtBadge(fmt: string): string {
		const f = fmt?.toLowerCase();
		if (f === 'epub') return 'accent'; if (f === 'pdf') return 'error'; if (f === 'mobi' || f === 'azw3') return 'warning'; return 'muted';
	}

	function statusClass(s: string): string {
		if (s === 'complete') return 'success'; if (s === 'downloading' || s === 'locating' || s === 'resolving' || s === 'queued') return 'accent'; if (s === 'error') return 'error'; return 'muted';
	}

	onMount(async () => {
		await fetchLibraries();
		// Load Kindle targets
		try {
			const kRes = await fetch('/api/kindle/targets', { credentials: 'include' });
			if (kRes.ok) { const kData = await kRes.json(); kindleTargets = kData.targets || []; kindleConfigured = kData.configured || false; }
		} catch { /* silent */ }
		const res = await fetch('/api/books/status', { credentials: 'include' }).catch(() => null);
		if (res?.ok) {
			const data = await res.json();
			const ids: string[] = [];
			for (const group of Object.values(data) as Record<string, Download>[]) {
				if (group && typeof group === 'object') { for (const [id, dl] of Object.entries(group)) { if (dl.status === 'complete') ids.push(id); } }
			}
			prevCompleted = new Set(ids);
		}
		fetchStatus();
		poll = setInterval(fetchStatus, 3000);
	});

	onDestroy(() => { if (poll) clearInterval(poll); });
</script>

{#if kindleConfigured && kindleTargets.length > 0}
	<div class="kindle-auto-row">
		<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
		<span class="kindle-auto-label">After download, also send to</span>
		<select class="kindle-auto-select" bind:value={autoKindleTarget}>
			<option value="none">No one (just download)</option>
			{#each kindleTargets as t}
				<option value={t.id}>{t.label}</option>
			{/each}
		</select>
	</div>
{/if}

<div class="search-bar">
	<div class="search-wrap">
		<svg class="s-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
		<input class="s-input" type="text" placeholder="Search books, authors, ISBNs..." bind:value={query} onkeydown={(e) => { if (e.key === 'Enter') search(); }} />
		{#if query}
			<button class="s-clear" onclick={() => { query = ''; results = []; searched = false; bookMeta = null; }}>
				<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
			</button>
		{/if}
	</div>
	<button class="s-btn" onclick={search} disabled={searching || !query.trim()}>
		{#if searching}<span class="spinner"></span>{:else}Search{/if}
	</button>
</div>

{#if searching}
	<div class="empty">Searching Anna's Archive, Libgen, Z-Library...</div>
{:else if searched && results.length === 0}
	<div class="empty">No results found for "{query}"</div>
{:else if results.length > 0}
	{#if bookMeta}<div class="result-count">{results.length} releases found</div>{/if}
	<div class="results-grid">
		{#each results as release (release.source_id)}
				{@const cover = coverUrl(release)}
				{@const isDl = downloadingIds.has(release.source_id) || !!downloads[release.source_id]}
				<div class="r-card">
					<div class="r-cover">
						{#if cover}
							<img src={cover} alt="" loading="lazy" onerror={(e) => (e.currentTarget as HTMLImageElement).style.display='none'} />
						{/if}
						<span class="fmt-badge {fmtBadge(release.format)}">{release.format.toUpperCase()}</span>
					</div>
					<div class="r-info">
						<div class="r-title">{release.title}</div>
						{#if release.extra?.author}<div class="r-author">{release.extra.author}</div>{/if}
						<div class="r-meta">
							{#if release.extra?.year}<span>{release.extra.year}</span>{/if}
							{#if release.size}<span>{release.size}</span>{/if}
							{#if release.language}<span>{release.language.toUpperCase()}</span>{/if}
						</div>
					</div>
					<div class="r-actions">
						{#if libraries.length > 0}
							<select class="lib-select" value={perBookLibrary[release.source_id] ?? defaultLibraryId} onchange={(e) => { perBookLibrary[release.source_id] = Number((e.currentTarget as HTMLSelectElement).value); perBookLibrary = {...perBookLibrary}; }}>
								{#each libraries as lib}<option value={lib.id}>{lib.name}</option>{/each}
							</select>
						{/if}
						{#if isDl}
							{@const dl = downloads[release.source_id]}
							{#if dl}
								<span class="dl-badge {statusClass(dl.status)}">{dl.status}</span>
								{#if dl.progress > 0 && dl.progress < 100}
									<div class="dl-bar"><div class="dl-fill" style="width:{dl.progress}%"></div></div>
								{/if}
							{:else}
								<span class="dl-badge muted">Queued...</span>
							{/if}
						{:else}
							<button class="dl-btn" onclick={() => download(release)}>
								<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
								Download
							</button>
						{/if}
					</div>
				</div>
		{/each}
	</div>
{:else}
	<div class="empty">
		<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" style="width:64px;height:64px;opacity:0.2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
		<div>Search for books</div>
		<div class="empty-sub">Anna's Archive, Libgen, Z-Library</div>
	</div>
{/if}

<DownloadStatusDock heading="Download queue" items={dockItems} on:action={handleDockAction} />

<style>
	.search-bar {
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		gap: 12px;
		margin-bottom: 22px;
		align-items: stretch;
	}

	.search-wrap { flex: 1; position: relative; }

	.s-icon {
		position: absolute;
		left: 16px;
		top: 50%;
		transform: translateY(-50%);
		width: 16px;
		height: 16px;
		color: #8a7d70;
		pointer-events: none;
	}

	.s-input {
		width: 100%;
		height: 50px;
		padding: 0 44px 0 44px;
		border-radius: 20px;
		border: 1px solid rgba(35, 26, 17, 0.1);
		background: rgba(255, 252, 248, 0.86);
		color: #1e1812;
		font-size: 0.98rem;
		font-family: var(--font);
		transition: border-color 160ms ease, box-shadow 160ms ease, background 160ms ease;
		box-sizing: border-box;
	}

	.s-input:focus {
		outline: none;
		border-color: rgba(47, 106, 82, 0.34);
		box-shadow: 0 0 0 4px rgba(47, 106, 82, 0.08);
		background: rgba(255, 253, 250, 0.98);
	}

	.s-input::placeholder { color: #8a7d70; }

	.s-clear {
		position: absolute;
		right: 12px;
		top: 50%;
		transform: translateY(-50%);
		background: none;
		border: none;
		cursor: pointer;
		color: #8a7d70;
		padding: 4px;
	}

	.s-clear svg { width: 14px; height: 14px; }

	.s-btn {
		height: 50px;
		padding: 0 20px;
		border-radius: 20px;
		background: #1f1a15;
		color: #fffaf5;
		border: none;
		font-size: 0.88rem;
		font-weight: 700;
		letter-spacing: 0.04em;
		text-transform: uppercase;
		cursor: pointer;
		font-family: var(--font);
		transition: transform 160ms ease, opacity 160ms ease, box-shadow 160ms ease;
		white-space: nowrap;
		box-shadow: 0 14px 28px rgba(35, 24, 15, 0.12);
	}

	.s-btn:disabled { opacity: 0.42; cursor: default; }

	.s-btn:hover:not(:disabled) {
		transform: translateY(-1px);
		box-shadow: 0 18px 34px rgba(35, 24, 15, 0.16);
	}
	.spinner { width: 16px; height: 16px; border: 2px solid rgba(255,255,255,0.3); border-top-color: white; border-radius: 50%; animation: spin 0.6s linear infinite; display: inline-block; }
	@keyframes spin { to { transform: rotate(360deg); } }

	.empty {
		padding: 52px 24px;
		text-align: center;
		color: #675b4f;
		font-size: 0.98rem;
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 8px;
		border-radius: 28px;
		border: 1px dashed rgba(35, 26, 17, 0.12);
		background: linear-gradient(180deg, rgba(255, 251, 246, 0.78), rgba(246, 240, 232, 0.58));
	}

	.empty-sub { font-size: 0.88rem; color: #8a7d70; }
	.result-count { font-size: 0.88rem; color: #675b4f; margin-bottom: 12px; }

	.results-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }

	.r-card {
		background: rgba(255, 252, 248, 0.9);
		border-radius: 26px;
		border: 1px solid rgba(35, 26, 17, 0.08);
		box-shadow: 0 20px 40px rgba(35, 24, 15, 0.06);
		overflow: hidden;
		display: flex;
		flex-direction: column;
	}

	.r-cover {
		position: relative;
		width: 100%;
		aspect-ratio: 3/2;
		background: linear-gradient(180deg, rgba(239, 231, 221, 0.9), rgba(247, 241, 233, 0.72));
		display: flex;
		align-items: center;
		justify-content: center;
		overflow: hidden;
	}
	.r-cover img { width: 100%; height: 100%; object-fit: cover; }
	.fmt-badge { position: absolute; top: 12px; right: 12px; font-size: 11px; font-weight: 700; padding: 4px 8px; border-radius: 999px; text-transform: uppercase; letter-spacing: 0.04em; }
	.fmt-badge.accent { background: var(--accent-dim); color: var(--accent); }
	.fmt-badge.error { background: var(--error-dim); color: var(--error); }
	.fmt-badge.warning { background: var(--warning-bg); color: var(--warning); }
	.fmt-badge.muted { background: var(--card-hover); color: var(--text-4); }
	.fmt-badge.sm { position: static; }
	.r-info { padding: 16px 18px 10px; flex: 1; }
	.r-title { font-size: 1rem; font-weight: 600; color: #1e1812; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
	.r-author { font-size: 0.9rem; color: #675b4f; margin-top: 3px; }
	.r-meta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; font-size: 11px; color: #8a7d70; text-transform: uppercase; letter-spacing: 0.04em; }
	.r-actions { padding: 0 18px 18px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
	.lib-select { padding: 9px 30px 9px 12px; border-radius: 14px; border: 1px solid rgba(35, 26, 17, 0.1); background: rgba(249, 244, 237, 0.92); color: #4a4036; font-size: 12px; font-family: var(--font); cursor: pointer; max-width: 140px; }
	.dl-btn { display: flex; align-items: center; gap: 6px; padding: 10px 14px; border-radius: 14px; background: #2f6a52; color: white; border: none; font-size: 0.84rem; font-weight: 700; cursor: pointer; font-family: var(--font); transition: transform 160ms ease, box-shadow 160ms ease; box-shadow: 0 12px 24px rgba(47, 106, 82, 0.2); }
	.dl-btn:hover { transform: translateY(-1px); box-shadow: 0 16px 28px rgba(47, 106, 82, 0.24); }
	.dl-btn svg { width: 14px; height: 14px; }
	.dl-badge { font-size: 11px; font-weight: 700; padding: 4px 8px; border-radius: 999px; text-transform: uppercase; letter-spacing: 0.04em; }
	.dl-badge.success { background: var(--success-dim); color: var(--success); }
	.dl-badge.accent { background: var(--accent-dim); color: var(--accent); }
	.dl-badge.error { background: var(--error-dim); color: var(--error); }
	.dl-badge.muted { background: var(--card-hover); color: var(--text-4); }
	.dl-bar { width: 80px; height: 4px; background: rgba(35, 26, 17, 0.1); border-radius: 999px; overflow: hidden; }
	.dl-bar.full { width: 100%; margin-top: 10px; }
	.dl-fill { height: 100%; background: var(--accent); border-radius: 2px; transition: width 0.3s ease; }

	.kindle-auto-row {
		display: flex; align-items: center; gap: 10px; margin-bottom: 16px;
		padding: 12px 14px; background: rgba(247, 241, 233, 0.82); border-radius: 20px; border: 1px solid rgba(35, 26, 17, 0.08);
		flex-wrap: wrap; max-width: 100%; overflow: hidden;
	}
	.kindle-auto-row svg { width: 14px; height: 14px; color: #675b4f; flex-shrink: 0; }
	.kindle-auto-label { font-size: 0.9rem; color: #4a4036; white-space: nowrap; flex-shrink: 1; min-width: 0; }
	.kindle-auto-select {
		height: 36px; padding: 0 30px 0 12px; border-radius: 14px; border: 1px solid rgba(35, 26, 17, 0.1);
		background: rgba(255, 252, 248, 0.94); color: #1e1812; font-size: 0.88rem; font-weight: 500; font-family: var(--font);
		cursor: pointer; -webkit-appearance: none; appearance: none;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b6b76' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
		background-repeat: no-repeat; background-position: right 10px center;
	}

	@media (max-width: 768px) {
		.search-bar { grid-template-columns: 1fr; }
		.results-grid { grid-template-columns: 1fr; }
	}
</style>
