<!--
	Image-Occlusion-Editor: Bild auswählen, mit Maus rechteckige Masken
	auf das Bild zeichnen, jede wird zu einem eigenen Review.

	Modell: alle Coordinaten in 0..1 relativ zum Bild — der Renderer
	skaliert auf die tatsächliche Display-Größe. So überleben Masken
	auch Browser-Resizing und Mobile-Display.
-->
<script lang="ts">
	import { type MaskRegion, parseMaskRegions } from '@cards/domain';
	import { uploadMedia } from '$lib/api/media.ts';
	import { API_BASE } from '$lib/api/client.ts';
	import { toasts } from '$lib/stores/toasts.svelte.ts';
	import { t } from '$lib/i18n/index.svelte.ts';

	let {
		imageRef = $bindable(''),
		maskRegionsJson = $bindable('[]'),
	}: {
		imageRef: string;
		maskRegionsJson: string;
	} = $props();

	let imgEl: HTMLImageElement | null = $state(null);
	let containerEl: HTMLDivElement | null = $state(null);
	let uploading = $state(false);
	let dragStart = $state<{ x: number; y: number } | null>(null);
	let dragCurrent = $state<{ x: number; y: number } | null>(null);

	const masks = $derived(parseMaskRegions(maskRegionsJson));
	const imageUrl = $derived(imageRef ? `${API_BASE}/api/v1/media/${imageRef}` : '');

	async function onFile(e: Event) {
		const input = e.currentTarget as HTMLInputElement;
		const file = input.files?.[0];
		if (!file) return;
		uploading = true;
		try {
			const r = await uploadMedia(file);
			if (r.kind !== 'image') {
				toasts.error(t('image_occlusion.not_an_image'));
				return;
			}
			imageRef = r.id;
			maskRegionsJson = '[]';
		} catch (err) {
			toasts.error(`${(err as Error).message}`);
		} finally {
			uploading = false;
			input.value = '';
		}
	}

	function rel(e: MouseEvent | PointerEvent): { x: number; y: number } | null {
		if (!containerEl) return null;
		const rect = containerEl.getBoundingClientRect();
		const x = (e.clientX - rect.left) / rect.width;
		const y = (e.clientY - rect.top) / rect.height;
		return { x: Math.max(0, Math.min(1, x)), y: Math.max(0, Math.min(1, y)) };
	}

	function onPointerDown(e: PointerEvent) {
		if (!imageRef) return;
		const p = rel(e);
		if (!p) return;
		dragStart = p;
		dragCurrent = p;
		(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
	}

	function onPointerMove(e: PointerEvent) {
		if (!dragStart) return;
		dragCurrent = rel(e);
	}

	function onPointerUp(e: PointerEvent) {
		if (!dragStart || !dragCurrent) {
			dragStart = null;
			dragCurrent = null;
			return;
		}
		const x = Math.min(dragStart.x, dragCurrent.x);
		const y = Math.min(dragStart.y, dragCurrent.y);
		const w = Math.abs(dragCurrent.x - dragStart.x);
		const h = Math.abs(dragCurrent.y - dragStart.y);

		// Ignoriere zu kleine Dragger (Klick statt Drag).
		if (w >= 0.02 && h >= 0.02) {
			const id = `m${Date.now().toString(36)}`;
			const next: MaskRegion = { id, x, y, w, h };
			maskRegionsJson = JSON.stringify([...masks, next]);
		}
		dragStart = null;
		dragCurrent = null;
		(e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId);
	}

	function onPointerCancel(e: PointerEvent) {
		dragStart = null;
		dragCurrent = null;
		(e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId);
	}

	function deleteMask(id: string) {
		maskRegionsJson = JSON.stringify(masks.filter((m) => m.id !== id));
	}

	function setLabel(id: string, label: string) {
		maskRegionsJson = JSON.stringify(
			masks.map((m) => (m.id === id ? { ...m, label: label || undefined } : m))
		);
	}

	const dragRect = $derived.by(() => {
		if (!dragStart || !dragCurrent) return null;
		return {
			x: Math.min(dragStart.x, dragCurrent.x),
			y: Math.min(dragStart.y, dragCurrent.y),
			w: Math.abs(dragCurrent.x - dragStart.x),
			h: Math.abs(dragCurrent.y - dragStart.y),
		};
	});
</script>

<div class="space-y-3">
	<label class="block text-sm">
		<span class="font-medium">{t('image_occlusion.image_label')}</span>
		<input
			type="file"
			accept="image/*"
			class="mt-1 block text-xs"
			disabled={uploading}
			onchange={onFile}
		/>
	</label>
	{#if uploading}
		<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{t('image_occlusion.uploading')}</p>
	{/if}

	{#if imageRef}
		<div
			bind:this={containerEl}
			class="relative inline-block max-w-full select-none touch-none"
			onpointerdown={onPointerDown}
			onpointermove={onPointerMove}
			onpointerup={onPointerUp}
			onpointercancel={onPointerCancel}
			role="application"
			aria-label={t('image_occlusion.canvas_aria')}
		>
			<img
				bind:this={imgEl}
				src={imageUrl}
				alt=""
				class="block max-w-full"
				draggable="false"
			/>
			<svg
				class="absolute inset-0 h-full w-full pointer-events-none"
				viewBox="0 0 1 1"
				preserveAspectRatio="none"
			>
				{#each masks as m (m.id)}
					<rect
						x={m.x}
						y={m.y}
						width={m.w}
						height={m.h}
						fill="rgba(255,180,0,0.6)"
						stroke="rgba(255,140,0,1)"
						stroke-width="0.005"
					/>
				{/each}
				{#if dragRect}
					<rect
						x={dragRect.x}
						y={dragRect.y}
						width={dragRect.w}
						height={dragRect.h}
						fill="rgba(0,140,255,0.4)"
						stroke="rgba(0,100,200,1)"
						stroke-width="0.005"
						stroke-dasharray="0.01,0.005"
					/>
				{/if}
			</svg>
		</div>

		<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{t('image_occlusion.draw_hint')}</p>

		{#if masks.length > 0}
			<ul class="space-y-2 text-sm">
				{#each masks as m, i (m.id)}
					<li class="flex items-center gap-3 rounded border border-[hsl(var(--color-border))] px-3 py-2">
						<span class="text-xs text-[hsl(var(--color-muted-foreground))] tabular-nums">{i + 1}</span>
						<input
							type="text"
							placeholder={t('image_occlusion.label_placeholder')}
							value={m.label ?? ''}
							oninput={(e) => setLabel(m.id, (e.currentTarget as HTMLInputElement).value)}
							class="flex-1 rounded border bg-[hsl(var(--color-card))] border-[hsl(var(--color-border))] px-2 py-1 text-sm"
						/>
						<button
							type="button"
							onclick={() => deleteMask(m.id)}
							class="text-xs text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-error))]"
							aria-label={t('image_occlusion.delete_mask')}
						>
							×
						</button>
					</li>
				{/each}
			</ul>
		{/if}
	{/if}
</div>
