<script lang="ts">
import { onMount, setContext, untrack } from 'svelte';
import { createPdfRenderer, type LibLoader } from '$lib/document/viewer/usePdfRenderer.svelte';
import PdfControls from './PdfControls.svelte';
import AnnotationLayer from '$lib/document/annotation/AnnotationLayer.svelte';
import type { Annotation } from '$lib/shared/types';
import { m } from '$lib/paraglide/messages.js';
import { parseBackendError, getErrorMessage } from '$lib/shared/errors';
import { csrfFetch } from '$lib/shared/cookies';

type DrawRect = { x: number; y: number; width: number; height: number; pageNumber: number };

let {
	url,
	documentId = '',
	transcribeMode = false,
	canAnnotate = false,
	blockNumbers = {},
	annotationReloadKey = 0,
	activeAnnotationId = $bindable<string | null>(null),
	onAnnotationClick,
	onTranscriptionDraw,
	onAnnotationFocus,
	documentFileHash,
	annotationsDimmed = false,
	flashAnnotationId = null,
	libLoader = undefined
}: {
	url: string;
	documentId?: string;
	transcribeMode?: boolean;
	canAnnotate?: boolean;
	blockNumbers?: Record<string, number>;
	annotationReloadKey?: number;
	activeAnnotationId?: string | null;
	onAnnotationClick?: (id: string) => void;
	onTranscriptionDraw?: (rect: DrawRect) => void;
	onAnnotationFocus?: (id: string) => void;
	documentFileHash?: string | null;
	annotationsDimmed?: boolean;
	flashAnnotationId?: string | null;
	libLoader?: LibLoader;
} = $props();

// untrack: libLoader prop change must not reinitialise the renderer
const renderer = untrack(() => createPdfRenderer(libLoader));

// Canvas and text layer container refs — bound via bind:this
let canvasEl = $state<HTMLCanvasElement | null>(null);
let textLayerEl = $state<HTMLDivElement | null>(null);

let annotations = $state<Annotation[]>([]);
let showAnnotations = $state(true);
let annotationUpdateError = $state<string | null>(null);

const TRANSCRIPTION_COLOR = '#00C7B1';

const visibleAnnotations = $derived(
	annotations.filter((a) => !a.fileHash || !documentFileHash || a.fileHash === documentFileHash)
);
const outdatedCount = $derived(annotations.length - visibleAnnotations.length);

onMount(async () => {
	await renderer.init();
});

$effect(() => {
	if (renderer.pdfjsReady && url) {
		renderer.loadDocument(url);
	}
});

// Wire DOM elements to the renderer and trigger rendering.
// canvasEl is read synchronously so Svelte tracks it as a dependency:
// when the canvas reappears after the loading spinner (loading → false),
// this effect re-fires and renders the already-loaded PDF.
$effect(() => {
	if (!canvasEl || !textLayerEl) return;
	renderer.setElements(canvasEl, textLayerEl);
	// Also track currentPage and scale so page-nav / zoom re-renders work.
	if (renderer.isLoaded && renderer.currentPage && renderer.scale > 0) {
		renderer.renderCurrentPage().then(() => renderer.prerender());
	}
});

$effect(() => {
	if (documentId && annotationReloadKey >= 0) {
		loadAnnotations(documentId);
	}
});

$effect(() => {
	if (transcribeMode) showAnnotations = true;
});

// Scroll-sync: when activeAnnotationId changes, navigate to its page
let prevActiveAnnotationId: string | null = null;
$effect(() => {
	const id = activeAnnotationId;
	if (!id || id === prevActiveAnnotationId || !renderer.isLoaded) {
		prevActiveAnnotationId = id;
		return;
	}
	prevActiveAnnotationId = id;

	const ann = annotations.find((a) => a.id === id);
	if (!ann) return;

	if (ann.pageNumber !== renderer.currentPage) {
		renderer.goToPage(ann.pageNumber);
	}

	requestAnimationFrame(() => {
		requestAnimationFrame(() => {
			const el = document.querySelector(`[data-testid="annotation-${id}"]`);
			el?.scrollIntoView({ behavior: 'smooth', block: 'center' });
		});
	});
});

async function loadAnnotations(docId: string) {
	if (!docId) return;
	try {
		const res = await fetch(`/api/documents/${docId}/annotations`);
		if (res.ok) {
			annotations = await res.json();
		}
	} catch {
		// ignore
	}
}

async function updateAnnotation(
	annotationId: string,
	coords: { x: number; y: number; width: number; height: number }
) {
	if (!documentId) return;
	const res = await csrfFetch(`/api/documents/${documentId}/annotations/${annotationId}`, {
		method: 'PATCH',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify(coords)
	});
	if (!res.ok) {
		const err = await parseBackendError(res);
		const msg = getErrorMessage(err?.code ?? 'ANNOTATION_UPDATE_FAILED');
		annotationUpdateError = msg;
		setTimeout(() => (annotationUpdateError = null), 4000);
		throw new Error(msg);
	}
	const updated = await res.json();
	annotations = annotations.map((a) => (a.id === annotationId ? updated : a));
}

setContext('annotationUpdate', updateAnnotation);

async function handleDraw(rect: { x: number; y: number; width: number; height: number }) {
	if (!documentId || !transcribeMode) return;
	await onTranscriptionDraw?.({ ...rect, pageNumber: renderer.currentPage });
	await loadAnnotations(documentId);
}

function handleAnnotationClick(id: string) {
	activeAnnotationId = id;
	onAnnotationClick?.(id);
}
</script>

{#if !url}
	<div class="flex h-full w-full items-center justify-center bg-pdf-bg text-ink-3">
		<p class="font-sans text-sm">Keine Datei vorhanden</p>
	</div>
{:else if renderer.error}
	<!-- role="alert" announces the failure to screen readers; the message text
	     (not colour alone) carries the meaning. The download link is the only
	     recovery action, so it is sized as a comfortable tap/focus target for
	     the archive's older readers. -->
	<div
		role="alert"
		class="flex h-full w-full flex-col items-center justify-center gap-3 bg-pdf-bg px-4 text-center text-ink-3"
	>
		<!-- A shape, not colour alone, signals the warning (WCAG 1.4.1). -->
		<svg
			class="h-10 w-10 text-red-400"
			viewBox="0 0 24 24"
			fill="none"
			stroke="currentColor"
			stroke-width="2"
			aria-hidden="true"
		>
			<path
				stroke-linecap="round"
				stroke-linejoin="round"
				d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
			/>
		</svg>
		<p class="font-sans text-base text-red-400">{m.doc_render_failed()}</p>
		<a
			href={url}
			target="_blank"
			rel="noopener noreferrer"
			class="inline-flex min-h-[44px] items-center px-3 py-2 font-sans text-sm text-primary underline hover:text-ink-2"
		>
			{m.doc_download_link()}
		</a>
	</div>
{:else}
	<div class="flex h-full w-full flex-col bg-pdf-bg">
		{#if outdatedCount > 0}
			<div
				class="flex shrink-0 items-center gap-2 border-b border-amber-500/30 bg-amber-500/10 px-4 py-2"
				data-testid="annotation-outdated-notice"
			>
				<svg
					class="h-4 w-4 shrink-0 text-amber-400"
					viewBox="0 0 24 24"
					fill="none"
					stroke="currentColor"
					stroke-width="2"
				>
					<path
						stroke-linecap="round"
						stroke-linejoin="round"
						d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
					/>
				</svg>
				<span class="font-sans text-xs text-amber-300">{m.annotation_outdated_notice()}</span>
			</div>
		{/if}
		{#if annotationUpdateError}
			<div
				class="flex shrink-0 items-center gap-2 border-b border-red-500/30 bg-red-500/10 px-4 py-2"
				aria-live="assertive"
				role="alert"
			>
				<svg
					class="h-4 w-4 shrink-0 text-red-400"
					viewBox="0 0 24 24"
					fill="none"
					stroke="currentColor"
					stroke-width="2"
				>
					<circle cx="12" cy="12" r="10" />
					<line x1="15" y1="9" x2="9" y2="15" />
					<line x1="9" y1="9" x2="15" y2="15" />
				</svg>
				<span class="font-sans text-xs text-red-300">{annotationUpdateError}</span>
			</div>
		{/if}

		<PdfControls
			currentPage={renderer.currentPage}
			totalPages={renderer.totalPages}
			isLoaded={renderer.isLoaded}
			showAnnotations={showAnnotations}
			annotationCount={annotations.length}
			onPrev={() => renderer.prevPage()}
			onNext={() => renderer.nextPage()}
			onZoomIn={() => renderer.zoomIn()}
			onZoomOut={() => renderer.zoomOut()}
			onToggleAnnotations={() => (showAnnotations = !showAnnotations)}
		/>

		<!-- PDF canvas area -->
		<div class="relative flex-1 overflow-auto">
			{#if renderer.loading}
				<div class="flex h-full items-center justify-center">
					<div
						class="h-8 w-8 animate-spin rounded-full border-2 border-white/20 border-t-white"
					></div>
				</div>
			{:else}
				<div class="flex min-h-full items-start justify-center p-4">
					<div
						class="pdf-page relative shadow-xl"
						data-page-number={renderer.currentPage}
						style="position: relative"
					>
						<canvas bind:this={canvasEl}></canvas>
						<div
							bind:this={textLayerEl}
							class="textLayer"
							style="position: absolute; top: 0; left: 0; overflow: hidden; pointer-events: none; line-height: 1;"
						></div>
						{#if showAnnotations}
							<AnnotationLayer
								annotations={visibleAnnotations.filter(
									(a) => a.pageNumber === renderer.currentPage
								)}
								canDraw={transcribeMode && canAnnotate}
								color={TRANSCRIPTION_COLOR}
								blockNumbers={blockNumbers}
								activeAnnotationId={activeAnnotationId}
								dimmed={annotationsDimmed}
								flashAnnotationId={flashAnnotationId}
								onDraw={handleDraw}
								onAnnotationClick={handleAnnotationClick}
								onAnnotationFocus={onAnnotationFocus}
							/>
						{/if}
					</div>
				</div>
			{/if}
		</div>
	</div>
{/if}
