<script lang="ts">
import { m } from '$lib/paraglide/messages.js';
import TranscriptionBlock from './TranscriptionBlock.svelte';
import OcrTrigger from '$lib/ocr/OcrTrigger.svelte';
import TranscribeCoachEmptyState from '$lib/shared/help/TranscribeCoachEmptyState.svelte';
import type { PersonMention, TranscriptionBlockData } from '$lib/shared/types';
import { createBlockAutoSave } from '$lib/document/transcription/useBlockAutoSave.svelte';
import { createBlockDragDrop } from '$lib/shared/hooks/useBlockDragDrop.svelte';
import { csrfFetch } from '$lib/shared/cookies';

type Props = {
	documentId: string;
	blocks: TranscriptionBlockData[];
	canComment: boolean;
	currentUserId: string | null;
	activeAnnotationId?: string | null;
	storedScriptType?: string;
	canRunOcr?: boolean;
	onBlockFocus: (blockId: string) => void;
	onSaveBlock: (blockId: string, text: string, mentionedPersons: PersonMention[]) => Promise<void>;
	onDeleteBlock: (blockId: string) => Promise<void>;
	onReviewToggle: (blockId: string) => Promise<void>;
	onMarkAllReviewed?: () => Promise<void>;
	onTriggerOcr?: (scriptType: string, useExistingAnnotations: boolean) => void;
	canWrite?: boolean;
	trainingLabels?: string[];
	onToggleTrainingLabel?: (label: string, enrolled: boolean) => Promise<void>;
};

let {
	documentId,
	blocks,
	canComment,
	currentUserId,
	activeAnnotationId = null,
	storedScriptType = '',
	canRunOcr = false,
	onBlockFocus,
	onSaveBlock,
	onDeleteBlock,
	onReviewToggle,
	onMarkAllReviewed,
	onTriggerOcr,
	canWrite = false,
	trainingLabels = [],
	onToggleTrainingLabel
}: Props = $props();

let activeBlockId: string | null = $state(null);
let localLabels: string[] = $derived.by(() => [...trainingLabels]);
let listEl: HTMLElement | null = $state(null);
let markingAllReviewed = $state(false);
let markAllError = $state<string | null>(null);

const sortedBlocks = $derived([...blocks].sort((a, b) => a.sortOrder - b.sortOrder));
const hasBlocks = $derived(blocks.length > 0);
const reviewedCount = $derived(blocks.filter((b) => b.reviewed).length);
const totalCount = $derived(blocks.length);
const reviewProgress = $derived(totalCount > 0 ? (reviewedCount / totalCount) * 100 : 0);
const allReviewed = $derived(totalCount > 0 && reviewedCount === totalCount);

// Sync: when an annotation is clicked on the PDF, activate the corresponding block
$effect(() => {
	if (!activeAnnotationId) return;
	const block = blocks.find((b) => b.annotationId === activeAnnotationId);
	if (block) activeBlockId = block.id;
});

async function handleMarkAllReviewed() {
	if (!onMarkAllReviewed) return;
	markingAllReviewed = true;
	markAllError = null;
	try {
		await onMarkAllReviewed();
	} catch {
		markAllError = m.transcription_mark_all_reviewed_error();
	} finally {
		markingAllReviewed = false;
	}
}

const autoSave = createBlockAutoSave({ saveFn: onSaveBlock, documentId });

const dragDrop = createBlockDragDrop({
	getSortedBlocks: () => sortedBlocks,
	onReorder: reorder
});

// Wire listEl to drag-drop module
$effect(() => {
	dragDrop.setListElement(listEl);
});

$effect(() => {
	function onBeforeUnload() {
		autoSave.flushOnUnload();
	}
	window.addEventListener('beforeunload', onBeforeUnload);
	return () => {
		window.removeEventListener('beforeunload', onBeforeUnload);
		autoSave.destroy();
	};
});

function handleFocus(blockId: string) {
	activeBlockId = blockId;
	onBlockFocus(blockId);
}

function handleDelete(blockId: string) {
	autoSave.clearBlock(blockId);
	onDeleteBlock(blockId);
}

async function reorder(newOrder: string[]) {
	try {
		const res = await csrfFetch(`/api/documents/${documentId}/transcription-blocks/reorder`, {
			method: 'PUT',
			headers: { 'Content-Type': 'application/json' },
			body: JSON.stringify({ blockIds: newOrder })
		});
		if (!res.ok) return;
		const updated = await res.json();
		for (const b of updated) {
			const existing = blocks.find((x) => x.id === b.id);
			if (existing) existing.sortOrder = b.sortOrder;
		}
	} catch {
		// ignore
	}
}

function handleMoveUp(blockId: string) {
	const sorted = [...sortedBlocks];
	const idx = sorted.findIndex((b) => b.id === blockId);
	if (idx <= 0) return;
	[sorted[idx - 1], sorted[idx]] = [sorted[idx], sorted[idx - 1]];
	reorder(sorted.map((b) => b.id));
}

function handleMoveDown(blockId: string) {
	const sorted = [...sortedBlocks];
	const idx = sorted.findIndex((b) => b.id === blockId);
	if (idx < 0 || idx >= sorted.length - 1) return;
	[sorted[idx], sorted[idx + 1]] = [sorted[idx + 1], sorted[idx]];
	reorder(sorted.map((b) => b.id));
}

async function handleLabelToggle(label: string) {
	if (!onToggleTrainingLabel) return;
	const enrolled = !localLabels.includes(label);
	if (enrolled) {
		localLabels = [...localLabels, label];
	} else {
		localLabels = localLabels.filter((l) => l !== label);
	}
	try {
		await onToggleTrainingLabel(label, enrolled);
	} catch {
		localLabels = [...trainingLabels];
	}
}
</script>

<div class="flex h-full flex-col overflow-y-auto bg-surface">
	{#if hasBlocks}
		<!-- Sticky review progress header -->
		<div class="sticky top-0 z-10 border-b border-line bg-surface px-4 pt-3 pb-2">
			<div class="flex items-center justify-between">
				<p class="font-sans text-xs text-ink-2">
					<span class="font-semibold text-ink">{reviewedCount} / {totalCount}</span> geprüft
				</p>
				{#if onMarkAllReviewed}
					<button
						onclick={handleMarkAllReviewed}
						disabled={allReviewed || markingAllReviewed}
						title={allReviewed ? m.transcription_mark_all_reviewed_disabled() : undefined}
						class="flex min-h-[44px] items-center gap-1.5 rounded-sm px-3 font-sans text-xs font-medium text-brand-navy/80 transition-colors hover:text-brand-navy focus-visible:ring-2 focus-visible:ring-brand-navy disabled:opacity-40"
					>
						{#if markingAllReviewed}
							<svg
								class="h-3.5 w-3.5 animate-spin"
								fill="none"
								viewBox="0 0 24 24"
								xmlns="http://www.w3.org/2000/svg"
								aria-hidden="true"
							>
								<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>
						{:else}
							<svg
								class="h-3.5 w-3.5"
								fill="none"
								stroke="currentColor"
								stroke-width="2"
								viewBox="0 0 24 24"
								xmlns="http://www.w3.org/2000/svg"
								aria-hidden="true"
							>
								<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
							</svg>
						{/if}
						{m.transcription_mark_all_reviewed()}
					</button>
				{/if}
			</div>
			<div class="bg-brand-sand mt-1.5 h-0.5 w-full overflow-hidden rounded-full">
				<div
					class="h-full rounded-full bg-brand-mint transition-all duration-300"
					style="width: {reviewProgress}%"
				></div>
			</div>
			{#if markAllError}
				<div
					role="alert"
					class="mt-1.5 flex items-center gap-2 rounded-sm border border-red-200 bg-red-50 px-3 py-2 font-sans text-sm text-red-700"
				>
					<span class="flex-1">{markAllError}</span>
					<button
						onclick={() => (markAllError = null)}
						aria-label={m.comp_dismiss()}
						class="flex min-h-[44px] min-w-[44px] items-center justify-center rounded text-red-600 hover:text-red-700 focus-visible:ring-2 focus-visible:ring-red-500"
					>
						<svg
							class="h-4 w-4"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
							viewBox="0 0 24 24"
							xmlns="http://www.w3.org/2000/svg"
							aria-hidden="true"
						>
							<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
						</svg>
					</button>
				</div>
			{/if}
		</div>
		<div class="p-4">
			<!-- svelte-ignore a11y_no_static_element_interactions -->
			<div
				class="flex flex-col gap-3"
				bind:this={listEl}
				onpointermove={dragDrop.handlePointerMove}
				onpointerup={dragDrop.handlePointerUp}
			>
				{#each sortedBlocks as block, i (block.id)}
					{#if dragDrop.dropTargetIdx === i}
						<div class="h-1 rounded-full bg-turquoise transition-all"></div>
					{/if}
					<!-- svelte-ignore a11y_no_static_element_interactions -->
					<div
						data-block-wrapper
						onblur={autoSave.handleBlur}
						onpointerdown={(e) => dragDrop.handleGripDown(e, block.id)}
						class="relative transition-all duration-150 {dragDrop.draggedBlockId === block.id ? 'z-10 rounded-lg shadow-xl ring-2 ring-turquoise/40' : ''}"
						style={dragDrop.draggedBlockId === block.id
							? `transform: translateY(${dragDrop.dragOffsetY}px) scale(1.02); opacity: 0.9;`
							: ''}
					>
						<TranscriptionBlock
							blockId={block.id}
							documentId={documentId}
							blockNumber={i + 1}
							text={block.text}
							mentionedPersons={block.mentionedPersons ?? []}
							label={block.label}
							active={activeBlockId === block.id}
							reviewed={block.reviewed ?? false}
							saveState={autoSave.getSaveState(block.id)}
							canComment={canComment}
							currentUserId={currentUserId}
							onTextChange={(text, mentions) =>
								autoSave.handleTextChange(block.id, text, mentions)}
							onFocus={() => handleFocus(block.id)}
							onDeleteClick={() => handleDelete(block.id)}
							onRetry={() =>
								autoSave.handleRetry(block.id, block.text, block.mentionedPersons ?? [])}
							onReviewToggle={() => onReviewToggle(block.id)}
							onMoveUp={() => handleMoveUp(block.id)}
							onMoveDown={() => handleMoveDown(block.id)}
							isFirst={i === 0}
							isLast={i === sortedBlocks.length - 1}
							source={block.source}
						/>
					</div>
				{/each}

				{#if dragDrop.dropTargetIdx === sortedBlocks.length}
					<div class="h-1 rounded-full bg-turquoise transition-all"></div>
				{/if}

				<!-- Next block CTA — dashed outline hint -->
				<div
					class="flex items-center justify-center rounded border border-dashed border-line px-4 py-5 text-center font-sans text-sm text-ink-3"
				>
					{m.transcription_next_block_cta({ number: sortedBlocks.length + 1 })}
				</div>

				{#if canRunOcr && onTriggerOcr}
					<div class="mt-6">
						<p class="mb-3 font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
							{m.ocr_section_heading()}
						</p>
						<div class="max-w-xs">
							<OcrTrigger
								blockCount={blocks.length}
								storedScriptType={storedScriptType}
								onTrigger={onTriggerOcr}
							/>
						</div>
					</div>
				{/if}
			</div>
		</div>
	{:else}
		<div class="p-4">
			<TranscribeCoachEmptyState />
		</div>
	{/if}

	{#if canWrite && hasBlocks}
		<div class="border-t border-line px-4 py-3">
			<p class="mb-2 font-sans text-xs font-medium text-ink-2">
				{m.transcribe_mark_for_training()}
			</p>
			<div class="flex flex-wrap gap-2">
				{#each [{ label: 'KURRENT_RECOGNITION', display: m.training_chip_kurrent() }, { label: 'KURRENT_SEGMENTATION', display: m.training_chip_segmentation() }] as chip (chip.label)}
					<button
						type="button"
						onclick={() => handleLabelToggle(chip.label)}
						class="rounded-full border px-3 py-1 font-sans text-xs font-medium transition-colors {localLabels.includes(chip.label)
							? 'border-brand-mint bg-brand-mint text-brand-navy'
							: 'border-line bg-surface text-ink-3 hover:border-brand-mint hover:text-brand-navy'}"
					>
						{chip.display}
					</button>
				{/each}
			</div>
		</div>
	{/if}
</div>
