<!--
	"Name new faces" — the missing half of the People feature. PhotoPrism
	only creates a person once someone names a detected face cluster, so a
	library can have tens of thousands of face markers and still show an
	empty People list. This panel surfaces the caller's unnamed clusters
	(scoped server-side to their BasePath) as face-crop cards with an
	inline name input; naming goes through PhotoPrism's own flow (PUT on
	the cluster's representative marker), which creates the Subject and
	propagates it across the cluster.
-->
<script lang="ts">
	import { createQuery, useQueryClient } from '@tanstack/svelte-query';
	import { toast } from 'svelte-sonner';
	import {
		listUnnamedFaces,
		nameFaceCluster,
		type UnnamedFaceCluster
	} from '$lib/services/photoprism';
	import { isAuthenticated, thumbUrl } from '$lib/stores/session.svelte';
	import { InlineLoader } from '$lib/components/feedback';
	import { UserPlus } from 'lucide-svelte';

	const qc = useQueryClient();

	const facesQuery = createQuery<UnnamedFaceCluster[]>(() => ({
		queryKey: ['faces', 'unnamed'],
		queryFn: listUnnamedFaces,
		enabled: isAuthenticated(),
		staleTime: 60_000
	}));

	let drafts = $state<Record<string, string>>({});
	let busy = $state<Record<string, boolean>>({});

	async function submit(cluster: UnnamedFaceCluster) {
		const name = (drafts[cluster.faceId] ?? '').trim();
		if (!name || busy[cluster.faceId]) return;
		busy[cluster.faceId] = true;
		try {
			await nameFaceCluster(cluster.markerUid, name);
			toast.success(`Named ${name}`, {
				description:
					'PhotoPrism links the whole cluster in the background — the photo count may keep growing.'
			});
			drafts[cluster.faceId] = '';
			void qc.invalidateQueries({ queryKey: ['faces', 'unnamed'] });
			void qc.invalidateQueries({ queryKey: ['subjects'] });
		} catch (err) {
			toast.error(err instanceof Error ? err.message : 'Naming failed');
		} finally {
			busy[cluster.faceId] = false;
		}
	}

	const clusters = $derived(facesQuery.data ?? []);
</script>

{#if facesQuery.isPending}
	<InlineLoader label="Looking for unnamed faces…" />
{:else if clusters.length > 0}
	<section class="space-y-3">
		<header class="space-y-0.5">
			<h2 class="flex items-center gap-1.5 text-sm font-medium text-foreground">
				<UserPlus class="h-4 w-4" /> Name new faces
			</h2>
			<p class="text-[11px] text-muted-foreground">
				Faces PhotoPrism detected but nobody has named yet. Naming one creates a person and tags
				every matching photo.
			</p>
		</header>
		<div
			class="grid gap-3"
			style="grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));"
		>
			{#each clusters as cluster (cluster.faceId)}
				<div
					class="flex flex-col items-center gap-2 rounded-md border border-border bg-card/30 p-3"
				>
					<div class="relative">
						<img
							src={thumbUrl(cluster.thumb, 'tile_224')}
							alt="Unnamed face"
							loading="lazy"
							decoding="async"
							class="h-20 w-20 rounded-full border border-border object-cover"
						/>
						<span
							class="absolute -bottom-1 -right-1 rounded-full bg-secondary px-1.5 py-0.5 text-[10px] font-medium tabular-nums text-muted-foreground"
							title={`${cluster.count} of your photos carry this face`}
						>
							{cluster.count}
						</span>
					</div>
					<input
						type="text"
						placeholder="Name…"
						disabled={busy[cluster.faceId]}
						class="w-full rounded border border-input bg-background px-1.5 py-1 text-center text-xs shadow-sm focus:outline-none focus:ring-1 focus:ring-ring disabled:opacity-50"
						bind:value={drafts[cluster.faceId]}
						onkeydown={(e) => {
							if (e.key === 'Enter') {
								e.preventDefault();
								void submit(cluster);
							}
						}}
						onblur={() => void submit(cluster)}
					/>
				</div>
			{/each}
		</div>
	</section>
{/if}
