<script lang="ts">
import type { components } from '$lib/generated/api';
import { m } from '$lib/paraglide/messages.js';
import { clickOutside } from '$lib/shared/actions/clickOutside';
import { formatDocumentDate, type DatePrecision } from '$lib/shared/utils/documentDate';
import { getLocale } from '$lib/paraglide/runtime.js';

type DocumentListItem = components['schemas']['DocumentListItem'];

/**
 * Exactly the fields this picker reads — id for selection/dedup, the rest for
 * the honest date label. A full `Document` and a `DocumentListItem` are both
 * structurally assignable, so the search results need no cast.
 */
type DocumentOption = Pick<
	DocumentListItem,
	'id' | 'title' | 'documentDate' | 'metaDatePrecision' | 'metaDateEnd'
>;

interface Props {
	selectedDocuments?: DocumentOption[];
	placeholder?: string;
	hiddenInputName?: string;
}

let {
	selectedDocuments = $bindable([]),
	placeholder = m.geschichte_editor_search_document(),
	hiddenInputName = 'documentIds'
}: Props = $props();

let searchTerm = $state('');
let results: DocumentOption[] = $state([]);
let showDropdown = $state(false);
let loading = $state(false);
let debounceTimer: ReturnType<typeof setTimeout>;
let inputEl: HTMLInputElement;
let dropdownStyle = $state('');

function updateDropdownPosition() {
	if (!inputEl) return;
	const rect = inputEl.getBoundingClientRect();
	dropdownStyle = `position:fixed;top:${rect.bottom + 4}px;left:${rect.left}px;width:${rect.width}px`;
}

function handleInput() {
	showDropdown = true;
	clearTimeout(debounceTimer);
	debounceTimer = setTimeout(async () => {
		if (searchTerm.length < 1) {
			results = [];
			return;
		}
		loading = true;
		try {
			const res = await fetch(`/api/documents/search?q=${encodeURIComponent(searchTerm)}&size=10`);
			if (res.ok) {
				const body: { items: DocumentListItem[] } = await res.json();
				const docs: DocumentOption[] = body.items.map((it) => ({
					id: it.id,
					title: it.title,
					documentDate: it.documentDate,
					metaDatePrecision: it.metaDatePrecision,
					metaDateEnd: it.metaDateEnd
				}));
				results = docs.filter((d) => !selectedDocuments.some((s) => s.id === d.id));
			}
		} catch {
			results = [];
		} finally {
			loading = false;
		}
	}, 300);
}

function selectDocument(doc: DocumentOption) {
	selectedDocuments = [...selectedDocuments, doc];
	searchTerm = '';
	showDropdown = false;
	results = [];
}

function removeDocument(id: string | undefined) {
	selectedDocuments = selectedDocuments.filter((d) => d.id !== id);
}

function formatDocLabel(doc: DocumentOption): string {
	if (!doc.documentDate) return doc.title;
	const label = formatDocumentDate(
		doc.documentDate,
		doc.metaDatePrecision as DatePrecision,
		doc.metaDateEnd,
		null,
		getLocale()
	);
	return `${doc.title} · ${label}`;
}
</script>

<svelte:window onscroll={updateDropdownPosition} onresize={updateDropdownPosition} />

{#each selectedDocuments as doc (doc.id)}
	<input type="hidden" name={hiddenInputName} value={doc.id} />
{/each}

<div class="relative" use:clickOutside onclickoutside={() => (showDropdown = false)}>
	<div
		class="flex min-h-[38px] flex-wrap gap-2 rounded border border-line bg-surface p-2 shadow-sm focus-within:ring-2 focus-within:ring-focus-ring focus-within:outline-none"
	>
		{#each selectedDocuments as doc (doc.id)}
			<span
				class="inline-flex items-center gap-1 rounded bg-muted px-2 py-1 text-sm font-medium text-ink"
			>
				{formatDocLabel(doc)}
				<button
					type="button"
					onclick={() => removeDocument(doc.id)}
					class="ml-0.5 text-ink/50 hover:text-red-500 focus:outline-none"
					aria-label={m.comp_multiselect_remove()}
				>
					<svg class="h-3 w-3" 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>
			</span>
		{/each}

		<input
			bind:this={inputEl}
			type="text"
			autocomplete="off"
			bind:value={searchTerm}
			oninput={handleInput}
			onfocus={() => {
				updateDropdownPosition();
				showDropdown = true;
			}}
			placeholder={placeholder}
			class="min-w-[120px] flex-1 border-none bg-transparent p-1 text-sm outline-none focus:ring-0"
		/>
	</div>

	{#if showDropdown && (results.length > 0 || loading)}
		<div
			style={dropdownStyle}
			class="ring-opacity-5 z-50 max-h-60 overflow-auto rounded-md bg-surface py-1 text-base shadow-lg ring-1 ring-black sm:text-sm"
		>
			{#if loading}
				<div class="p-2 text-sm text-ink-2">{m.comp_multiselect_loading()}</div>
			{:else}
				{#each results as doc (doc.id)}
					<div
						class="cursor-pointer py-2 pr-9 pl-3 text-ink select-none hover:bg-muted"
						onclick={() => selectDocument(doc)}
						onkeydown={(e) => e.key === 'Enter' && selectDocument(doc)}
						role="button"
						tabindex="0"
					>
						{formatDocLabel(doc)}
					</div>
				{/each}
			{/if}
		</div>
	{/if}
</div>
