<script lang="ts">
import { tick } from 'svelte';
import { m } from '$lib/paraglide/messages.js';

export interface FileEntry {
	id: string;
	/** Present in upload mode only. Edit mode entries reference an existing
	 *  document by `documentId` and have no local file blob. */
	file?: File;
	/** Present in edit mode only — the server-side document UUID being edited. */
	documentId?: string;
	title: string;
	status: 'idle' | 'error';
	previewUrl: string;
}

let {
	files,
	activeId,
	onSelect,
	onRemove
}: {
	files: FileEntry[];
	activeId: string;
	onSelect: (id: string) => void;
	onRemove: (id: string) => void;
} = $props();

let trackEl = $state<HTMLDivElement | null>(null);
let listEl = $state<HTMLUListElement | null>(null);

const activeAnnouncement = $derived(files.find((f) => f.id === activeId)?.title ?? '');

function scrollPrev() {
	trackEl?.scrollBy({ left: -120, behavior: 'smooth' });
}
function scrollNext() {
	trackEl?.scrollBy({ left: 120, behavior: 'smooth' });
}

async function handleRemove(entry: FileEntry, index: number) {
	const targetId = index > 0 ? files[index - 1].id : (files[index + 1]?.id ?? null);
	onRemove(entry.id);
	if (targetId) {
		await tick();
		(listEl?.querySelector<HTMLElement>(`[data-chip-id="${targetId}"]`) ?? null)?.focus();
	}
}

$effect(() => {
	if (!listEl) return;
	const node = listEl;

	function handleKeyDown(event: KeyboardEvent) {
		const buttons = Array.from(node.querySelectorAll<HTMLElement>('[data-chip-id]'));
		if (buttons.length === 0) return;

		const focusedIndex = buttons.indexOf(document.activeElement as HTMLElement);
		if (focusedIndex === -1) return;

		if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
			event.preventDefault();
			const nextIndex = (focusedIndex + 1) % buttons.length;
			buttons[nextIndex].focus();
		} else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
			event.preventDefault();
			const prevIndex = (focusedIndex - 1 + buttons.length) % buttons.length;
			buttons[prevIndex].focus();
		}
	}

	node.addEventListener('keydown', handleKeyDown);
	return () => node.removeEventListener('keydown', handleKeyDown);
});
</script>

<div aria-live="polite" aria-atomic="true" class="sr-only">{activeAnnouncement}</div>
<div
	data-testid="file-switcher-strip"
	class="flex h-11 shrink-0 items-center gap-1 border-t border-line bg-pdf-ctrl px-2"
>
	<button
		type="button"
		aria-label={m.bulk_switcher_prev()}
		onclick={scrollPrev}
		class="flex h-[44px] w-[44px] shrink-0 items-center justify-center rounded-sm text-sm text-ink-3 hover:bg-black/10 hover:text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
		>‹</button
	>

	<!-- Gradient fade overlays signal hidden overflow to pointer-only users -->
	<div class="relative flex flex-1 overflow-hidden">
		<div
			class="pointer-events-none absolute inset-y-0 left-0 z-10 w-6 bg-gradient-to-r from-pdf-ctrl to-transparent"
		></div>
		<div
			class="pointer-events-none absolute inset-y-0 right-0 z-10 w-6 bg-gradient-to-l from-pdf-ctrl to-transparent"
		></div>
		<div bind:this={trackEl} class="flex flex-1 gap-1 overflow-x-auto" style="scrollbar-width:none">
			<ul bind:this={listEl} role="list" class="flex flex-row gap-1 py-1">
				{#each files as entry, i (entry.id)}
					<li role="listitem" class="inline-flex shrink-0 items-center">
						<button
							type="button"
							tabindex="0"
							aria-current={entry.id === activeId ? 'true' : undefined}
							data-status={entry.status}
							data-chip-id={entry.id}
							onclick={() => onSelect(entry.id)}
							class={[
							'inline-flex cursor-pointer items-center gap-1 rounded-[2px] px-1.5 py-0.5 text-xs font-bold transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent',
							entry.id === activeId
								? 'bg-accent text-primary'
								: 'bg-black/[0.06] text-ink-2 hover:bg-black/10',
							entry.status === 'error'
								? '!border !border-dashed !border-red-400 !bg-red-50/80 !text-red-700'
								: ''
						].join(' ')}
						>
							<span
								class={[
								'rounded-[2px] px-0.5 text-[11px] font-extrabold opacity-85',
								entry.id === activeId ? 'bg-black/20' : 'bg-black/10'
							].join(' ')}
								>{i + 1}</span
							>
							<span class="max-w-[8rem] truncate" title={entry.title}>{entry.title}</span>
							{#if entry.status === 'error'}
								<span class="sr-only">{m.bulk_file_error_chip_label()}</span>
								<span aria-hidden="true" class="ml-0.5 font-extrabold text-red-600">!</span>
							{/if}
						</button>
						<button
							type="button"
							aria-label={m.bulk_remove_file()}
							data-remove-id={entry.id}
							onclick={() => handleRemove(entry, i)}
							class="ml-0.5 flex h-[44px] w-[44px] items-center justify-center text-base text-ink-3 hover:text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
						>
							×
						</button>
					</li>
				{/each}
			</ul>
		</div>
	</div>

	<button
		type="button"
		aria-label={m.bulk_switcher_next()}
		onclick={scrollNext}
		class="flex h-[44px] w-[44px] shrink-0 items-center justify-center rounded-sm text-sm text-ink-3 hover:bg-black/10 hover:text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
		>›</button
	>
</div>
