<script lang="ts">
	import { invalidateAll } from '$app/navigation';
	import type { PageData } from './$types';
	import * as m from '$lib/paraglide/messages.js';

	let { data }: { data: PageData } = $props();

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

	type Shelf = '' | 'plan_to_read' | 'completed' | 'dropped';
	let activeShelf = $state<Shelf | 'all'>('all');

	const shelfLabels: Record<string, string> = {
		all: 'All',
		'': 'Reading',
		plan_to_read: 'Plan to Read',
		completed: 'Completed',
		dropped: 'Dropped'
	};

	const shelfMap = $derived(data.shelfMap as Record<string, string>);
	const filteredBooks = $derived(
		activeShelf === 'all'
			? data.books
			: data.books.filter((b) => (shelfMap[b.slug] ?? '') === activeShelf)
	);

	const shelfCounts = $derived({
		all: data.books.length,
		'': data.books.filter((b) => (shelfMap[b.slug] ?? '') === '').length,
		plan_to_read: data.books.filter((b) => shelfMap[b.slug] === 'plan_to_read').length,
		completed: data.books.filter((b) => shelfMap[b.slug] === 'completed').length,
		dropped: data.books.filter((b) => shelfMap[b.slug] === 'dropped').length
	});

	// ── Selection / bulk-action state ─────────────────────────────────────────
	let selectMode = $state(false);
	let selected = $state<Set<string>>(new Set());
	let busy = $state(false);
	let shelfPickerOpen = $state(false);

	const selectedCount = $derived(selected.size);
	const allVisibleSelected = $derived(
		filteredBooks.length > 0 && filteredBooks.every((b) => selected.has(b.slug))
	);

	function enterSelectMode(slug: string) {
		selectMode = true;
		selected = new Set([slug]);
	}

	function exitSelectMode() {
		selectMode = false;
		selected = new Set();
		shelfPickerOpen = false;
	}

	function toggleSelect(slug: string) {
		const next = new Set(selected);
		if (next.has(slug)) next.delete(slug);
		else next.add(slug);
		selected = next;
		if (next.size === 0) exitSelectMode();
	}

	function toggleSelectAll() {
		if (allVisibleSelected) {
			selected = new Set();
			exitSelectMode();
		} else {
			selected = new Set(filteredBooks.map((b) => b.slug));
		}
	}

	// Long-press support (pointer events, works on desktop + mobile)
	let longPressTimer: ReturnType<typeof setTimeout> | null = null;
	let longPressFired = false;

	function onPointerDown(slug: string) {
		if (selectMode) return;
		longPressFired = false;
		longPressTimer = setTimeout(() => {
			longPressFired = true;
			enterSelectMode(slug);
		}, 500);
	}

	function onPointerUp() {
		if (longPressTimer) {
			clearTimeout(longPressTimer);
			longPressTimer = null;
		}
	}

	function onPointerCancel() {
		if (longPressTimer) {
			clearTimeout(longPressTimer);
			longPressTimer = null;
		}
	}

	// Prevent navigation click if long-press just fired
	function onCardClick(e: MouseEvent, slug: string) {
		if (selectMode) {
			e.preventDefault();
			toggleSelect(slug);
			return;
		}
		if (longPressFired) {
			e.preventDefault();
			longPressFired = false;
		}
	}

	// ── Bulk actions ──────────────────────────────────────────────────────────
	async function bulkRemove() {
		if (busy || selected.size === 0) return;
		busy = true;
		try {
			await fetch('/api/library/bulk-remove', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({ slugs: [...selected] })
			});
			await invalidateAll();
		} finally {
			busy = false;
			exitSelectMode();
		}
	}

	async function bulkMoveShelf(shelf: Shelf) {
		if (busy || selected.size === 0) return;
		busy = true;
		shelfPickerOpen = false;
		try {
			await fetch('/api/library/bulk-shelf', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({ slugs: [...selected], shelf })
			});
			await invalidateAll();
		} finally {
			busy = false;
			exitSelectMode();
		}
	}
</script>

<svelte:head>
	<title>{m.books_page_title()}</title>
</svelte:head>

<div class="mb-6 flex items-start justify-between gap-4">
	<div>
		<h1 class="text-2xl font-bold text-(--color-text)">{m.books_heading()}</h1>
		<p class="text-(--color-muted) text-sm mt-1">
			{m.books_count({ n: String(data.books?.length ?? 0), s: (data.books?.length ?? 0) !== 1 ? 's' : '' })}
		</p>
	</div>
	{#if selectMode}
		<button
			type="button"
			onclick={exitSelectMode}
			class="text-sm text-(--color-muted) hover:text-(--color-text) transition-colors pt-1"
		>
			Cancel
		</button>
	{/if}
</div>

{#if !data.books?.length}
	<div class="text-center py-20 text-(--color-muted)">
		<p class="text-lg">{m.books_empty_library()}</p>
		<p class="text-sm mt-2">
			{m.books_empty_discover()}
			<a href="/catalogue" class="text-(--color-brand) hover:text-(--color-brand-dim) transition-colors">{m.books_empty_discover_link()}</a>
			{m.books_empty_discover_suffix()}
		</p>
	</div>
{:else}
	<!-- Shelf tabs + select-all row -->
	<div class="flex items-center gap-2 mb-4 flex-wrap">
		{#if selectMode}
			<button
				type="button"
				onclick={toggleSelectAll}
				class="px-3 py-1.5 rounded-full text-sm font-medium transition-colors
					bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text) border border-(--color-border)"
			>
				{allVisibleSelected ? 'Deselect all' : 'Select all'}
			</button>
			<span class="text-sm text-(--color-muted)">{selectedCount} selected</span>
		{:else}
			{#each (['all', '', 'plan_to_read', 'completed', 'dropped'] as const) as shelf}
				{#if shelfCounts[shelf] > 0 || shelf === 'all'}
					<button
						type="button"
						onclick={() => (activeShelf = shelf)}
						class="px-3 py-1.5 rounded-full text-sm font-medium transition-colors
							{activeShelf === shelf
								? 'bg-(--color-brand) text-(--color-surface)'
								: 'bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text) border border-(--color-border)'}"
					>
						{shelfLabels[shelf]}{shelfCounts[shelf] !== data.books.length || shelf === 'all' ? ` (${shelfCounts[shelf]})` : ''}
					</button>
				{/if}
			{/each}
		{/if}
	</div>

	<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4">
		{#each filteredBooks as book}
			{@const lastChapter = data.progressMap[book.slug]}
			{@const genres = parseGenres(book.genres)}
			{@const bookShelf = shelfMap[book.slug] ?? ''}
			{@const isSelected = selected.has(book.slug)}
			<!-- svelte-ignore a11y_no_static_element_interactions -->
			<a
				href="/books/{book.slug}"
				onclick={(e) => onCardClick(e, book.slug)}
				onpointerdown={() => onPointerDown(book.slug)}
				onpointerup={onPointerUp}
				onpointercancel={onPointerCancel}
				draggable="false"
				class="group relative flex flex-col rounded-lg overflow-hidden bg-(--color-surface-2) border transition-colors select-none
					{isSelected
						? 'border-(--color-brand) ring-2 ring-(--color-brand)/40'
						: 'border-(--color-border) hover:bg-(--color-surface-3) hover:border-zinc-500'}"
			>
				<!-- Selection overlay -->
				{#if selectMode}
					<div class="absolute top-1.5 left-1.5 z-10 w-5 h-5 rounded-full border-2 flex items-center justify-center transition-colors
						{isSelected ? 'bg-(--color-brand) border-(--color-brand)' : 'bg-black/40 border-white/60'}">
						{#if isSelected}
							<svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7" />
							</svg>
						{/if}
					</div>
				{/if}

				<!-- Cover image -->
				<div class="aspect-[2/3] bg-(--color-surface) overflow-hidden">
					{#if book.cover}
						<img
							src={book.cover}
							alt={book.title}
							class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300 {selectMode ? 'pointer-events-none' : ''}"
							loading="lazy"
							draggable="false"
						/>
					{:else}
						<div class="w-full h-full flex items-center justify-center text-(--color-muted)">
							<svg class="w-12 h-12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
									d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
							</svg>
						</div>
					{/if}
				</div>

				<!-- Info -->
				<div class="p-2 flex flex-col gap-1 flex-1">
					<h2 class="text-xs font-semibold text-(--color-text) line-clamp-2 leading-snug">
						{book.title ?? ''}
					</h2>
					{#if book.author}
						<p class="text-xs text-(--color-muted) truncate">{book.author ?? ''}</p>
					{/if}

					<div class="mt-auto pt-1 flex items-center justify-between gap-1">
						{#if book.status}
							<span class="text-xs px-1.5 py-0.5 rounded bg-(--color-surface-3) text-(--color-text) truncate max-w-[60%]">
								{book.status}
							</span>
						{/if}
						{#if lastChapter}
							<span class="text-xs text-(--color-brand) font-medium ml-auto whitespace-nowrap">
								ch.{lastChapter}
							</span>
						{/if}
					</div>
					{#if bookShelf && activeShelf === 'all'}
						<span class="text-xs px-1.5 py-0.5 rounded bg-(--color-surface-3) text-(--color-muted) self-start">
							{shelfLabels[bookShelf] ?? bookShelf}
						</span>
					{/if}

					{#if genres.length > 0}
						<div class="flex flex-wrap gap-1 mt-1">
							{#each genres.slice(0, 2) as genre}
								<span class="text-xs px-1 py-0.5 rounded bg-(--color-surface) text-(--color-muted)">{genre}</span>
							{/each}
						</div>
					{/if}
				</div>
			</a>
		{/each}
	</div>
{/if}

<!-- Bulk action bar (sticky bottom, shown in selection mode) -->
{#if selectMode}
	<div class="fixed bottom-0 left-0 right-0 z-50 bg-(--color-surface-2) border-t border-(--color-border) px-4 py-3 flex items-center gap-3 shadow-lg">
		<span class="text-sm text-(--color-muted) mr-auto">
			{selectedCount} selected
		</span>

		<!-- Move to shelf picker -->
		<div class="relative">
			<button
				type="button"
				disabled={busy || selectedCount === 0}
				onclick={() => (shelfPickerOpen = !shelfPickerOpen)}
				class="px-3 py-2 rounded-lg text-sm font-medium transition-colors
					bg-(--color-surface-3) text-(--color-text) border border-(--color-border)
					hover:border-zinc-500 disabled:opacity-40 disabled:cursor-not-allowed flex items-center gap-1.5"
			>
				<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="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
				</svg>
				Move to shelf
				<svg class="w-3.5 h-3.5 transition-transform {shelfPickerOpen ? 'rotate-180' : ''}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
				</svg>
			</button>

			{#if shelfPickerOpen}
				<div class="absolute bottom-full mb-2 right-0 bg-(--color-surface-2) border border-(--color-border) rounded-lg shadow-xl overflow-hidden min-w-[160px]">
					{#each ([['', 'Reading'], ['plan_to_read', 'Plan to Read'], ['completed', 'Completed'], ['dropped', 'Dropped']] as const) as [val, label]}
						<button
							type="button"
							onclick={() => bulkMoveShelf(val as Shelf)}
							class="w-full text-left px-4 py-2.5 text-sm text-(--color-text) hover:bg-(--color-surface-3) transition-colors"
						>
							{label}
						</button>
					{/each}
				</div>
			{/if}
		</div>

		<!-- Remove button -->
		<button
			type="button"
			disabled={busy || selectedCount === 0}
			onclick={bulkRemove}
			class="px-3 py-2 rounded-lg text-sm font-medium transition-colors
				bg-red-500/10 text-red-400 border border-red-500/30
				hover:bg-red-500/20 disabled:opacity-40 disabled:cursor-not-allowed flex items-center gap-1.5"
		>
			{#if busy}
				<svg class="w-4 h-4 animate-spin" 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-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
						d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
				</svg>
			{/if}
			Remove
		</button>
	</div>

	<!-- Spacer so last row of cards isn't hidden behind the action bar -->
	<div class="h-20"></div>
{/if}
