<script lang="ts">
import { onMount } from 'svelte';
import type { PDFDocumentProxy, PDFPageProxy, RenderTask } from 'pdfjs-dist';
import AnnotationLayer from './AnnotationLayer.svelte';

let {
	url,
	documentId = '',
	canAnnotate = false
}: {
	url: string;
	documentId?: string;
	canAnnotate?: boolean;
} = $props();

let pdfDoc = $state<PDFDocumentProxy | null>(null);
let currentPage = $state(1);
let totalPages = $state(0);
let scale = $state(1.5);
let loading = $state(false);
let error = $state<string | null>(null);

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

// Internal mutable refs for in-flight tasks — NOT $state to avoid reactive loops
let renderTask: RenderTask | null = null;
let textLayerInstance: { cancel: () => void } | null = null;

// Holds the dynamically-loaded pdfjs module (browser-only)
// Not $state — we use pdfjsReady as the reactive trigger instead
let pdfjsLib: typeof import('pdfjs-dist') | null = null;
let pdfjsReady = $state(false);

type Annotation = {
	id: string;
	documentId: string;
	pageNumber: number;
	x: number;
	y: number;
	width: number;
	height: number;
	color: string;
	createdAt: string;
};

let annotations = $state<Annotation[]>([]);
let annotateMode = $state(false);
let annotateColor = $state('#ffff00');

onMount(async () => {
	// Dynamic import keeps pdfjs out of the SSR bundle entirely
	const [lib, { default: workerUrl }] = await Promise.all([
		import('pdfjs-dist'),
		import('pdfjs-dist/build/pdf.worker.min.mjs?url')
	]);
	lib.GlobalWorkerOptions.workerSrc = workerUrl;
	pdfjsLib = lib;
	pdfjsReady = true;
});

async function loadDocument(src: string) {
	if (!pdfjsLib) return;
	loading = true;
	error = null;
	pdfDoc = null;
	currentPage = 1;
	totalPages = 0;

	try {
		const loadingTask = pdfjsLib.getDocument(src);
		const doc = await loadingTask.promise;
		pdfDoc = doc;
		totalPages = doc.numPages;
	} catch (e) {
		error = e instanceof Error ? e.message : 'Failed to load PDF';
	} finally {
		loading = false;
	}
}

async function renderPage(doc: PDFDocumentProxy, pageNum: number) {
	if (!pdfjsLib || !canvasEl || !textLayerEl) return;

	// Cancel any in-flight render
	if (renderTask) {
		renderTask.cancel();
		renderTask = null;
	}
	if (textLayerInstance) {
		textLayerInstance.cancel();
		textLayerInstance = null;
	}

	let page: PDFPageProxy;
	try {
		page = await doc.getPage(pageNum);
	} catch {
		return;
	}

	const dpr = window.devicePixelRatio || 1;
	const viewport = page.getViewport({ scale: scale * dpr });

	const canvas = canvasEl;
	const ctx = canvas.getContext('2d');
	if (!ctx) return;

	canvas.width = viewport.width;
	canvas.height = viewport.height;
	canvas.style.width = `${viewport.width / dpr}px`;
	canvas.style.height = `${viewport.height / dpr}px`;

	const task = page.render({ canvas, canvasContext: ctx, viewport });
	renderTask = task;

	try {
		await task.promise;
	} catch (e: unknown) {
		if (
			typeof e === 'object' &&
			e !== null &&
			'name' in e &&
			(e as { name: string }).name === 'RenderingCancelledException'
		)
			return;
		return;
	}
	renderTask = null;

	// Text layer
	const textDiv = textLayerEl;
	textDiv.innerHTML = '';
	textDiv.style.width = `${viewport.width / dpr}px`;
	textDiv.style.height = `${viewport.height / dpr}px`;

	const tl = new pdfjsLib.TextLayer({
		textContentSource: page.streamTextContent(),
		container: textDiv,
		viewport
	});
	textLayerInstance = tl;
	try {
		await tl.render();
	} catch {
		// cancelled
	}
}

async function prerender(doc: PDFDocumentProxy, pageNum: number) {
	const neighbors = [pageNum - 1, pageNum + 1].filter((n) => n >= 1 && n <= doc.numPages);
	for (const n of neighbors) {
		try {
			await doc.getPage(n);
		} catch {
			// ignore
		}
	}
}

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 handleAnnotationDraw(rect: { x: number; y: number; width: number; height: number }) {
	if (!documentId) return;
	try {
		const res = await fetch(`/api/documents/${documentId}/annotations`, {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({
				pageNumber: currentPage,
				x: rect.x,
				y: rect.y,
				width: rect.width,
				height: rect.height,
				color: annotateColor
			})
		});
		if (res.ok) {
			const created: Annotation = await res.json();
			annotations = [...annotations, created];
		}
	} catch {
		// ignore
	}
}

async function handleAnnotationDelete(annotationId: string) {
	if (!documentId) return;
	try {
		const res = await fetch(`/api/documents/${documentId}/annotations/${annotationId}`, {
			method: 'DELETE'
		});
		if (res.ok) {
			annotations = annotations.filter((a) => a.id !== annotationId);
		}
	} catch {
		// ignore
	}
}

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

$effect(() => {
	// Read scale synchronously so Svelte tracks it as a dependency.
	// Without this, zoom changes don't re-trigger the effect because
	// scale is only read inside the async renderPage call.
	if (pdfDoc && currentPage && scale > 0) {
		renderPage(pdfDoc, currentPage).then(() => {
			if (pdfDoc) prerender(pdfDoc, currentPage);
		});
	}
});

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

function prevPage() {
	if (currentPage > 1) currentPage -= 1;
}

function nextPage() {
	if (pdfDoc && currentPage < pdfDoc.numPages) currentPage += 1;
}

function zoomIn() {
	scale += 0.25;
}

function zoomOut() {
	if (scale > 0.5) scale -= 0.25;
}
</script>

{#if !url}
	<div class="flex h-full w-full items-center justify-center bg-[#2A2A2A] text-gray-400">
		<p class="font-sans text-sm">Keine Datei vorhanden</p>
	</div>
{:else if error}
	<div
		class="flex h-full w-full flex-col items-center justify-center gap-3 bg-[#2A2A2A] text-gray-300"
	>
		<p class="font-sans text-sm text-red-400">Fehler beim Laden der PDF</p>
		<a
			href={url}
			target="_blank"
			rel="noopener noreferrer"
			class="font-sans text-xs text-brand-mint underline hover:opacity-80"
		>
			Direkt öffnen
		</a>
	</div>
{:else}
	<div class="flex h-full w-full flex-col bg-[#2A2A2A]">
		<!-- Controls -->
		<div
			class="flex shrink-0 items-center justify-between gap-2 border-b border-white/10 px-4 py-2"
		>
			<!-- Page navigation -->
			<div class="flex items-center gap-2">
				<button
					onclick={prevPage}
					disabled={currentPage <= 1}
					aria-label="Zurück"
					class="rounded p-1 text-gray-300 transition hover:bg-white/10 disabled:opacity-40"
				>
					<svg
						class="h-4 w-4"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
					</svg>
				</button>

				{#if totalPages > 0}
					<span class="font-sans text-xs text-gray-300 tabular-nums">
						{currentPage} / {totalPages}
					</span>
				{/if}

				<button
					onclick={nextPage}
					disabled={!pdfDoc || currentPage >= totalPages}
					aria-label="Weiter"
					class="rounded p-1 text-gray-300 transition hover:bg-white/10 disabled:opacity-40"
				>
					<svg
						class="h-4 w-4"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
					</svg>
				</button>
			</div>

			<!-- Zoom controls -->
			<div class="flex items-center gap-1">
				<button
					onclick={zoomOut}
					aria-label="Verkleinern"
					class="rounded p-1 text-gray-300 transition hover:bg-white/10"
				>
					<svg
						class="h-4 w-4"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<circle cx="11" cy="11" r="8" /><path
							stroke-linecap="round"
							d="M21 21l-4.35-4.35M8 11h6"
						/>
					</svg>
				</button>
				<button
					onclick={zoomIn}
					aria-label="Vergrößern"
					class="rounded p-1 text-gray-300 transition hover:bg-white/10"
				>
					<svg
						class="h-4 w-4"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<circle cx="11" cy="11" r="8" /><path
							stroke-linecap="round"
							d="M21 21l-4.35-4.35M11 8v6M8 11h6"
						/>
					</svg>
				</button>
			</div>

			<!-- Annotate controls -->
			{#if canAnnotate}
				<div class="flex items-center gap-1">
					<button
						onclick={() => (annotateMode = !annotateMode)}
						aria-label={annotateMode ? 'Annotieren beenden' : 'Annotieren'}
						class="rounded px-2 py-1 font-sans text-xs text-gray-300 transition hover:bg-white/10 {annotateMode ? 'bg-white/20' : ''}"
					>
						{annotateMode ? 'Fertig' : 'Annotieren'}
					</button>
					{#if annotateMode}
						<input
							type="color"
							bind:value={annotateColor}
							aria-label="Farbe wählen"
							class="h-6 w-6 cursor-pointer rounded border-0 bg-transparent p-0"
							title="Farbe wählen"
						/>
					{/if}
				</div>
			{:else}
				<button
					disabled
					title="Sie benötigen die Berechtigung ANNOTATE_ALL zum Annotieren"
					class="cursor-not-allowed rounded px-2 py-1 font-sans text-xs text-gray-500"
					aria-label="Annotieren (keine Berechtigung)"
				>
					Annotieren
				</button>
			{/if}
		</div>

		<!-- PDF canvas area -->
		<div class="relative flex-1 overflow-auto">
			{#if 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={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>
						<AnnotationLayer
							annotations={annotations.filter((a) => a.pageNumber === currentPage)}
							canAnnotate={annotateMode}
							color={annotateColor}
							onDraw={handleAnnotationDraw}
							onDelete={handleAnnotationDelete}
						/>
					</div>
				</div>
			{/if}
		</div>
	</div>
{/if}
