<!--
	Single photo tile — shared by the timeline (+page.svelte) and the flat
	drill-in grids (PhotoGrid.svelte) so the tile chrome is single-sourced
	and can't drift between the two. Owns: thumbnail, selection animation,
	favorite/video badges, and the hover-only "open preview" affordance.

	Sizing is delegated to the caller — PhotoTile fills its container with
	h-full w-full, so the timeline can wrap it in its windowing shell and
	PhotoGrid can wrap it in an aspect-square cell.

	Click semantics:
	  - plain click  → onClick   (caller decides; both surfaces use it for select-only)
	  - dblclick     → onDblclick (caller usually opens preview)
	  - Maximize ico → onOpenPreview (single-click fallback for the
	                                  not-yet-discovered dblclick gesture)
-->
<script lang="ts">
	import { Maximize2 } from 'lucide-svelte';
	import { thumbSrc, thumbSrcSet } from '$lib/stores/session.svelte';
	import { view } from '$lib/stores/view.svelte';
	import { isVideo, primaryFile, type PpPhoto } from '$lib/types/photoprism';

	interface Props {
		photo: PpPhoto;
		selected: boolean;
		onClick: (e: MouseEvent) => void;
		onDblclick: (e: MouseEvent) => void;
		onOpenPreview: () => void;
	}
	let { photo, selected, onClick, onDblclick, onOpenPreview }: Props = $props();

	const hash = $derived(photo.Hash ?? primaryFile(photo).Hash);
	// Render-size hint for the browser's srcset picker. `view.thumbnailSize`
	// is the grid's `minmax(<px>, 1fr)` minimum — real tiles may be a hair
	// wider when the grid stretches to fill the column, but `tile_*` is
	// discrete (100/224/500) so the rounding-up at the next variant
	// boundary swallows the difference.
	const tilePx = $derived(view.thumbnailSize);
	const src1x = $derived(thumbSrc(hash, tilePx));
	const srcset = $derived(thumbSrcSet(hash, tilePx));
</script>

<!--
	Wrapper div carries `group` so the Maximize affordance can hover-reveal
	off the same hover region as the image-scale hover. The Maximize button
	sits as a sibling of the main tile button (not nested) — nesting <button>
	in <button> is invalid HTML.
-->
<div class="group relative h-full w-full">
	<!--
		Selection animation: scale to 90% + blue ring + blue tint overlay,
		driven by a springy `cubic-bezier(0.34, 1.3, 0.64, 1)` over 300ms.
		The overshoot is intentionally modest — on a *shrink* a larger
		overshoot reads as the tile dipping below 90% before settling, which
		is disorienting when many tiles transition at once (range-select).
		The transition class is only applied while selected so deselect
		snaps back instantly instead of crawling.

		The keyboard-focused tile gets the same treatment as a selected one,
		so the arrow-key cursor reads as a "selection of one".
	-->
	<button
		type="button"
		data-tile
		data-uid={photo.UID}
		onclick={onClick}
		ondblclick={onDblclick}
		title="Click to select · Double-click to open"
		class:scale-90={selected}
		class:ring-2={selected}
		class:ring-blue-500={selected}
		class:ring-offset-2={selected}
		class:ring-offset-background={selected}
		class:transition-[transform,box-shadow]={selected}
		class:duration-300={selected}
		class:ease-[cubic-bezier(0.34,1.3,0.64,1)]={selected}
		class="relative h-full w-full overflow-hidden rounded-md border border-border bg-secondary p-0 outline-none focus:outline-none"
	>
		<img
			src={src1x}
			srcset={srcset}
			alt={photo.OriginalName ?? photo.FileName ?? photo.Name ?? 'Photo'}
			loading="lazy"
			decoding="async"
			class="h-full w-full object-cover"
			class:transition={!selected}
			class:group-hover:scale-105={!selected}
		/>
		{#if selected}
			<div class="pointer-events-none absolute inset-0 bg-blue-500/40"></div>
		{/if}
		{#if photo.Favorite}
			<span
				class="absolute right-1.5 top-1.5 rounded bg-background/80 px-1 text-xs text-red-500"
				>♥</span
			>
		{/if}
		{#if isVideo(photo)}
			<span
				class="absolute left-1.5 top-1.5 rounded bg-background/80 px-1 text-[10px] font-medium text-foreground"
				>VIDEO</span
			>
		{/if}
	</button>
	<!--
		Hover-only "open preview" affordance. Single-click on this icon
		opens the preview directly, giving users a one-click fallback for
		the dblclick gesture (and a visual cue that previewing is a thing
		at all). Hidden when the tile is selected — there'd be no preview
		intent on a tile the user is in the middle of bulk-acting on.
	-->
	{#if !selected}
		<button
			type="button"
			class="absolute bottom-1.5 right-1.5 hidden rounded bg-background/80 p-1 text-muted-foreground hover:text-foreground group-hover:block"
			onclick={(e) => {
				e.stopPropagation();
				onOpenPreview();
			}}
			title="Open preview"
			aria-label="Open preview"
		>
			<Maximize2 class="h-3 w-3" />
		</button>
	{/if}
</div>
