<script lang="ts">
import { onMount, onDestroy, tick } from 'svelte';
import { m } from '$lib/paraglide/messages.js';
import { page } from '$app/state';
import { replaceState } from '$app/navigation';
import DocumentTopBar from '$lib/document/DocumentTopBar.svelte';
import DocumentViewer from '$lib/document/DocumentViewer.svelte';
import TranscriptionEditView from '$lib/document/transcription/TranscriptionEditView.svelte';
import TranscriptionReadView from '$lib/document/transcription/TranscriptionReadView.svelte';
import TranscriptionPanelHeader from '$lib/document/transcription/TranscriptionPanelHeader.svelte';
import ShortcutCheatsheet from '$lib/document/transcription/ShortcutCheatsheet.svelte';
import { transcribeShortcuts } from '$lib/shared/actions/transcribeShortcuts';
import { createOcrJob } from '$lib/ocr/useOcrJob.svelte';
import { createTranscriptionBlocks } from '$lib/document/transcription/useTranscriptionBlocks.svelte';
import { stepRegion } from '$lib/document/transcription/regionNavigation';
import { resolveTrainingMark } from '$lib/document/transcription/trainingMark';
import { canArmDraw, shouldDisarmDraw } from '$lib/document/transcription/drawCue';
import { createFileLoader } from '$lib/document/viewer/useFileLoader.svelte';
import { scrollToCommentFromQuery } from '$lib/shared/utils/deepLinkScroll';
import { getConfirmService } from '$lib/shared/services/confirm.svelte';

let { data } = $props();

const { confirm } = getConfirmService();

const doc = $derived(data.document);
const canWrite = $derived(data.canWrite ?? false);
const currentUserId = $derived((data.user?.id as string | undefined) ?? null);

// ── File loading ──────────────────────────────────────────────────────────────

const fileLoader = createFileLoader();

$effect(() => {
	if (doc?.id && doc?.filePath) {
		fileLoader.loadFile(`/api/documents/${doc.id}/file`);
	}
});

onDestroy(() => fileLoader.destroy());

// ── Mode state ───────────────────────────────────────────────────────────────

let transcribeMode = $state(false);
let panelMode = $state<'read' | 'edit'>('read');
let activeAnnotationId = $state<string | null>(null);
let highlightBlockId = $state<string | null>(null);
let flashAnnotationId = $state<string | null>(null);
let pdfStripExpanded = $state(false);
let cheatsheetOpen = $state(false);
let drawArmed = $state(false);
// Flag set by the deep-link helper so the transcribe-mode $effect does not
// overwrite the panelMode it picked (e.g. forcing 'edit' on notification
// click-through). One-shot: consumed after the effect's loadBlocks resolves.
let skipInitialPanelMode = $state(false);

const prefersReducedMotion = $derived(
	typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
);

// ── Transcription blocks ─────────────────────────────────────────────────────

const transcription = createTranscriptionBlocks({
	documentId: () => doc?.id ?? ''
});

async function handleAnnotationDeleteRequest(annotationId: string) {
	const confirmed = await confirm({
		title: m.transcription_block_delete_confirm(),
		destructive: true
	});
	if (!confirmed) return;
	await transcription.deleteAnnotation(annotationId);
}

const ocrJob = createOcrJob({
	documentId: () => doc?.id ?? '',
	onJobFinished: async () => {
		await transcription.load();
		transcription.bumpAnnotationReloadKey();
		panelMode = canWrite && !transcription.hasBlocks ? 'edit' : 'read';
	}
});

async function triggerOcr(scriptType: string, useExistingAnnotations: boolean = false) {
	await ocrJob.triggerOcr(scriptType, useExistingAnnotations);
}

async function createBlockFromDraw(rect: {
	x: number;
	y: number;
	width: number;
	height: number;
	pageNumber: number;
}) {
	drawArmed = false;
	const created = await transcription.createFromDraw(rect);
	if (created) {
		activeAnnotationId = created.annotationId;
	}
}

// ── Keyboard shortcuts (issue #327) ──────────────────────────────────────────

const sortedBlocks = $derived([...transcription.blocks].sort((a, b) => a.sortOrder - b.sortOrder));

function goToRegion(delta: 1 | -1) {
	const ids = sortedBlocks.map((b) => b.annotationId);
	const next = stepRegion(ids, activeAnnotationId, delta);
	if (next) activeAnnotationId = next;
}

function toggleMode() {
	if (canWrite) panelMode = panelMode === 'read' ? 'edit' : 'read';
}

function toggleTrainingMark() {
	const toggle = resolveTrainingMark(activeAnnotationId, doc.trainingLabels ?? []);
	if (toggle) transcription.toggleTrainingLabel(toggle.label, toggle.enrolled);
}

function deleteCurrentRegion() {
	if (activeAnnotationId) handleAnnotationDeleteRequest(activeAnnotationId);
}

// Disarm the draw cue whenever we leave edit mode.
$effect(() => {
	if (shouldDisarmDraw(panelMode)) drawArmed = false;
});

const shortcutOptions = {
	isPanelOpen: () => transcribeMode,
	isCheatsheetOpen: () => cheatsheetOpen,
	panelMode: () => panelMode,
	goToNextRegion: () => goToRegion(1),
	goToPrevRegion: () => goToRegion(-1),
	toggleMode,
	closePanel: () => (transcribeMode = false),
	startDrawMode: () => {
		if (canArmDraw(panelMode)) drawArmed = true;
	},
	toggleTrainingMark,
	deleteCurrentRegion,
	openCheatsheet: () => (cheatsheetOpen = true)
};

function handleBlockFocus(blockId: string) {
	const block = transcription.blocks.find((b) => b.id === blockId);
	if (block) {
		activeAnnotationId = block.annotationId;
	}
}

async function handleAnnotationClick(annotationId: string) {
	activeAnnotationId = annotationId;

	if (!transcribeMode) {
		transcribeMode = true;
		await transcription.load();
	}

	// In read mode, highlight the matching paragraph
	const block = transcription.findByAnnotationId(annotationId);
	if (block) {
		highlightBlockId = block.id;
		setTimeout(
			() => {
				highlightBlockId = null;
			},
			prefersReducedMotion ? 2000 : 1500
		);
	}

	// Wait for DOM to render, then scroll to the matching block
	const scrollBehavior = prefersReducedMotion ? 'instant' : 'smooth';
	requestAnimationFrame(() => {
		if (block) {
			const el = document.querySelector(`[data-block-id="${block.id}"]`);
			el?.scrollIntoView({ behavior: scrollBehavior, block: 'nearest' });
		}
	});
}

function handleParagraphClick(annotationId: string) {
	activeAnnotationId = annotationId;
	flashAnnotationId = annotationId;
	pdfStripExpanded = true;
	setTimeout(
		() => {
			flashAnnotationId = null;
		},
		prefersReducedMotion ? 2000 : 1500
	);
}

// Load blocks and check OCR status when transcribe mode is entered
$effect(() => {
	if (transcribeMode) {
		transcription.load().then(() => {
			if (skipInitialPanelMode) {
				skipInitialPanelMode = false;
			} else {
				panelMode = canWrite && !transcription.hasBlocks ? 'edit' : 'read';
			}
		});
		if (canWrite) ocrJob.checkStatus();
	}
});

// ── Navigation / init ─────────────────────────────────────────────────────────

let navHeight = $state(0);

async function waitForPanelRender(): Promise<void> {
	await tick();
	await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
}

onMount(() => {
	navHeight = document.querySelector('header')?.getBoundingClientRect().height ?? 0;

	if (doc?.id) {
		localStorage.setItem(
			'familienarchiv.lastVisited',
			JSON.stringify({ id: doc.id, title: doc.title ?? '' })
		);
	}

	if (page.url.searchParams.get('task') === 'transcribe') {
		transcribeMode = true;
		tick()
			.then(() => {
				const closeBtn = document.querySelector<HTMLElement>('[data-testid="panel-close"]');
				closeBtn?.scrollIntoView({
					behavior: prefersReducedMotion ? 'instant' : 'smooth',
					block: 'nearest'
				});
				closeBtn?.focus({ preventScroll: true });
				replaceState(page.url.pathname, page.state ?? {});
			})
			.catch((e) => console.error('task deep-link failed', e));
	}

	scrollToCommentFromQuery(new URL(page.url), {
		transcribeMode,
		setTranscribeMode: (v) => (transcribeMode = v),
		setPanelMode: (m) => {
			skipInitialPanelMode = true;
			panelMode = m;
		},
		loadBlocks: () => transcription.load(),
		setActiveAnnotationId: (id) => (activeAnnotationId = id),
		flashAnnotation: (annotationId) => {
			flashAnnotationId = annotationId;
			setTimeout(() => (flashAnnotationId = null), prefersReducedMotion ? 2000 : 1500);
		},
		prefersReducedMotion,
		afterTick: waitForPanelRender,
		getElement: (id) => document.getElementById(id),
		onStripUrl: () => replaceState(page.url.pathname, page.state ?? {})
	}).catch((e) => console.error('deep-link scroll failed', e));

	// Esc is owned solely by the transcribeShortcuts action (issue #327, decision
	// B1) — no competing inline keydown listener here.
	return () => {
		ocrJob.destroy();
	};
});
</script>

<svelte:head>
	<title>{doc.title || doc.originalFilename || 'Dokument'}</title>
</svelte:head>

<div
	class="fixed right-0 bottom-0 left-0 z-40 flex flex-col overflow-hidden bg-surface"
	style="top: {navHeight}px"
	data-hydrated
	use:transcribeShortcuts={shortcutOptions}
>
	<DocumentTopBar
		doc={doc}
		canWrite={canWrite}
		fileUrl={fileLoader.fileUrl}
		bind:transcribeMode={transcribeMode}
		inferredRelationship={data.inferredRelationship}
		geschichten={data.geschichten ?? []}
		canBlogWrite={data.canBlogWrite ?? false}
	/>

	<div class="relative flex-1 overflow-hidden {transcribeMode ? 'flex flex-col md:flex-row' : ''}">
		<div
			class={transcribeMode
				? `relative flex-1 overflow-hidden md:min-h-0 ${panelMode === 'read' ? (pdfStripExpanded ? 'max-h-[50vh] min-h-[50vh] md:max-h-none md:min-h-0' : 'max-h-[70px] min-h-[70px] md:max-h-none md:min-h-0') : 'min-h-[40vh]'} transition-[min-height,max-height] duration-300`
				: 'absolute inset-0'}
		>
			<DocumentViewer
				doc={doc}
				fileUrl={fileLoader.fileUrl}
				isLoading={fileLoader.isLoading}
				error={fileLoader.fileError}
				transcribeMode={transcribeMode && !ocrJob.running}
				canAnnotate={canWrite}
				blockNumbers={transcription.blockNumbers}
				annotationReloadKey={transcription.annotationReloadKey}
				annotationsDimmed={transcribeMode && panelMode === 'read'}
				flashAnnotationId={flashAnnotationId}
				bind:activeAnnotationId={activeAnnotationId}
				onAnnotationClick={handleAnnotationClick}
				onTranscriptionDraw={createBlockFromDraw}
				onAnnotationFocus={(id) => (activeAnnotationId = id)}
			/>
		</div>

		{#if transcribeMode && panelMode === 'read'}
			<button
				type="button"
				onclick={() => (pdfStripExpanded = !pdfStripExpanded)}
				class="flex h-7 w-full shrink-0 items-center justify-center border-t border-line bg-muted text-xs font-semibold text-ink-2 md:hidden"
				aria-label={pdfStripExpanded ? m.scan_collapse() : m.scan_expand()}
			>
				<svg
					class="mr-1 h-3 w-3 transition-transform duration-200 {pdfStripExpanded ? 'rotate-180' : ''}"
					viewBox="0 0 24 24"
					fill="none"
					stroke="currentColor"
					stroke-width="2.5"
					aria-hidden="true"
				>
					<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
				</svg>
				{pdfStripExpanded ? m.scan_collapse() : m.scan_expand()}
			</button>
		{/if}

		{#if transcribeMode}
			<div
				class="flex min-h-0 flex-1 shrink-0 flex-col border-t border-line md:w-[400px] md:flex-none md:border-t-0 md:border-l lg:w-[480px]"
			>
				<TranscriptionPanelHeader
					mode={panelMode}
					hasBlocks={transcription.hasBlocks}
					blockCount={transcription.blocks.length}
					lastEditedAt={transcription.lastEditedAt}
					canEdit={canWrite}
					onModeChange={(newMode) => {
						if (canWrite) panelMode = newMode;
					}}
					onClose={() => (transcribeMode = false)}
				/>
				<div class="flex-1 overflow-y-auto">
					{#if ocrJob.errorMessage}
						<div class="mx-4 mt-4 rounded-sm border border-red-200 bg-red-50 px-4 py-3">
							<p class="text-sm text-red-700">{ocrJob.errorMessage}</p>
						</div>
					{/if}
					{#if ocrJob.running}
						<div class="flex flex-1 flex-col items-center justify-center px-6 py-12 text-center">
							<svg
								class="mb-4 h-8 w-8 animate-spin text-brand-mint"
								viewBox="0 0 24 24"
								fill="none"
							>
								<circle
									class="opacity-25"
									cx="12"
									cy="12"
									r="10"
									stroke="currentColor"
									stroke-width="4"
								></circle>
								<path
									class="opacity-75"
									fill="currentColor"
									d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
								></path>
							</svg>
							<p class="text-xs font-bold tracking-widest text-gray-400 uppercase">
								{m.ocr_progress_heading()}
							</p>
							<p class="mt-2 text-sm text-ink-2">
								{ocrJob.progressMessage}
							</p>
							{#if ocrJob.skippedPages > 0}
								<p class="mt-1 text-xs text-amber-600">
									{ocrJob.skippedPages} Seiten übersprungen
								</p>
							{/if}
						</div>
					{:else if panelMode === 'read'}
						<TranscriptionReadView
							blocks={transcription.blocks}
							highlightBlockId={highlightBlockId}
							onParagraphClick={handleParagraphClick}
						/>
					{:else}
						<TranscriptionEditView
							documentId={doc.id}
							blocks={transcription.blocks}
							canComment={canWrite}
							currentUserId={currentUserId}
							activeAnnotationId={activeAnnotationId}
							storedScriptType={doc.scriptType ?? ''}
							canRunOcr={canWrite && !!doc.filePath}
							canWrite={canWrite}
							trainingLabels={doc.trainingLabels ?? []}
							onBlockFocus={handleBlockFocus}
							onSaveBlock={transcription.save}
							onDeleteBlock={transcription.delete}
							onReviewToggle={transcription.reviewToggle}
							onMarkAllReviewed={transcription.markAllReviewed}
							onTriggerOcr={triggerOcr}
							onToggleTrainingLabel={transcription.toggleTrainingLabel}
						/>
					{/if}
				</div>
			</div>
		{/if}
	</div>

	{#if drawArmed}
		<div
			class="pointer-events-none absolute bottom-4 left-1/2 z-50 -translate-x-1/2 rounded-full bg-ink px-4 py-2 font-sans text-xs text-white shadow-lg"
			role="status"
		>
			{m.shortcut_draw_hint()}
		</div>
	{/if}

	<ShortcutCheatsheet open={cheatsheetOpen} onClose={() => (cheatsheetOpen = false)} />
</div>
