<script lang="ts">
	import * as Command from '$lib/components/ui/command/index';
	import * as Kbd from '$lib/components/ui/kbd/index.js';
	import * as InputGroup from '$lib/components/ui/input-group/index.js';
	import { Badge } from '$lib/components/ui/badge/index';
	import { Spinner } from '$lib/components/ui/spinner/index.js';
	import SearchIcon from '@lucide/svelte/icons/search';
	import { listBooks } from '$lib/api';
	import { getLibraryState } from '$lib/state/library.svelte';
	import type { PaginatedResponse, Book } from '$lib/schema';
	import BookImage from '../view/book-image.svelte';
	import { goto } from '$app/navigation';

	const libraryState = getLibraryState();

	let open = $state(false);
	let searchString = $state('');
	let searchResult = $state<PaginatedResponse<Book>>();
	let debounceTimeout = $state<NodeJS.Timeout | undefined>(undefined);
	let isLoading = $state(false);

	async function handleInputChange() {
		if (!searchString || searchString === '') {
			clearTimeout(debounceTimeout);
			searchResult = undefined;
			isLoading = false;
			return;
		}

		clearTimeout(debounceTimeout);

		isLoading = true;

		debounceTimeout = setTimeout(async () => {
			try {
				const results = await listBooks({
					libraries: [libraryState.activeLibrary!.id],
					searchString
				});
				searchResult = results || [];
			} finally {
				isLoading = false;
			}
		}, 300);
	}

	function handleKeydown(e: KeyboardEvent) {
		if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
			e.preventDefault();
			open = !open;
		}
	}

	function handleClick(e: any) {
		open = true;
		e.target.blur();
	}
</script>

<svelte:document onkeydown={handleKeydown} />

<form>
	<div class="flex w-full max-w-xs flex-col gap-6">
		<InputGroup.Root>
			<InputGroup.Input placeholder="Search..." onclick={handleClick} />
			<InputGroup.Addon>
				<SearchIcon />
			</InputGroup.Addon>
			<InputGroup.Addon align="inline-end">
				<Kbd.Root>Ctrl</Kbd.Root>+
				<Kbd.Root>k</Kbd.Root>
			</InputGroup.Addon>
		</InputGroup.Root>
	</div>
</form>

<Command.Dialog bind:open shouldFilter={false}>
	<Command.Input
		placeholder="Search for a book..."
		oninput={handleInputChange}
		bind:value={searchString}
	/>
	{#if searchString && searchResult?.items.length === 0 && !isLoading}
		<div class="flex h-24 items-center justify-center">
			<span class="text-sm">No results found.</span>
		</div>
	{/if}
	{#if isLoading}
		<div class="flex h-24 items-center justify-center">
			<Spinner class="scale-150" />
		</div>
	{:else}
		<Command.List class="max-h-[600px]">
			{#if searchResult?.items.length > 0}
				<Command.Group heading="Books">
					{#each searchResult?.items as book (book.id)}
						<Command.Item
							value={String(book.id)}
							onSelect={() => {
								goto(`/book/${book.id}`);
								open = false;
							}}
							class="cursor-pointer"
						>
							<div class="hover:bg-base-200 flex gap-4 p-3">
								<BookImage
									src="/api/{book.cover_image}"
									class="w-24 rounded object-cover shadow-lg"
								/>
								<div class="flex-top flex flex-col">
									<span class="text-lg font-medium">{book.title}</span>
									<span class=" text-md">{book.subtitle}</span>
									{#if book.authors.length > 0}
										<span class="line-clamp-1 w-full text-sm text-muted-foreground">
											by
											{#each book.authors as author}
												<a
													href="/library/{libraryState.activeLibrary!.id}/view?authors={author.id}"
													class="cs-list hover:underline">{author.name}</a
												> &thinsp;
											{/each}
										</span>
									{/if}
									<div class="mt-1 ml-[-1.5] flex">
										{#each book.tags as tag}
											<Badge class="scale-75">{tag.name}</Badge>
										{/each}
									</div>
								</div>
							</div>
						</Command.Item>
					{/each}
				</Command.Group>
			{/if}
		</Command.List>
	{/if}
</Command.Dialog>
