<script lang="ts">
	import type { PageProps } from './$types';

	const { data }: PageProps = $props();
	const { article, audioUrl } = data;

	// -------------------------------------------------------------------------
	// Body parsing: split into paragraphs → sentences → tokens
	// -------------------------------------------------------------------------

	type WordToken = { type: 'word'; text: string; wordIdx: number };
	type OtherToken = { type: 'other'; text: string };
	type Token = WordToken | OtherToken;

	type Sentence = {
		tokens: Token[];
		idx: number; // global sentence index
		startWordIdx: number;
		endWordIdx: number;
	};

	type Paragraph = { sentences: Sentence[] };

	function parseBody(text: string): { paragraphs: Paragraph[]; totalWords: number } {
		const paragraphs: Paragraph[] = [];
		let wordIdx = 0;
		let sentenceIdx = 0;

		for (const paraText of text.split(/\n\n+/)) {
			if (!paraText.trim()) continue;

			// Split into alternating word / non-word tokens
			const rawTokens = paraText.match(/[\p{L}\p{N}\u2019'''-]+|[^\p{L}\p{N}\u2019'''-]+/gu) ?? [];

			const sentences: Sentence[] = [];
			let currentTokens: Token[] = [];
			let startWordIdx = wordIdx;
			let hasWord = false;

			for (const raw of rawTokens) {
				if (/[\p{L}\p{N}]/u.test(raw)) {
					currentTokens.push({ type: 'word', text: raw, wordIdx: wordIdx++ });
					hasWord = true;
				} else {
					currentTokens.push({ type: 'other', text: raw });
					// Flush sentence on sentence-ending punctuation
					if (hasWord && /[.!?]/.test(raw)) {
						sentences.push({
							tokens: [...currentTokens],
							idx: sentenceIdx++,
							startWordIdx,
							endWordIdx: wordIdx - 1
						});
						currentTokens = [];
						startWordIdx = wordIdx;
						hasWord = false;
					}
				}
			}

			if (currentTokens.length > 0) {
				sentences.push({
					tokens: currentTokens,
					idx: sentenceIdx++,
					startWordIdx,
					endWordIdx: wordIdx - 1
				});
			}

			if (sentences.length > 0) {
				paragraphs.push({ sentences });
			}
		}

		return { paragraphs, totalWords: wordIdx };
	}

	const { paragraphs } = parseBody(article.target_body);

	// Flat sentence list for O(n) audio-time lookup
	const allSentences: Array<{ idx: number; startWordIdx: number; endWordIdx: number }> = [];
	for (const para of paragraphs) {
		for (const s of para.sentences) {
			allSentences.push({ idx: s.idx, startWordIdx: s.startWordIdx, endWordIdx: s.endWordIdx });
		}
	}

	// -------------------------------------------------------------------------
	// Transcript: extract per-word timings from Deepgram response
	// -------------------------------------------------------------------------

	type WordTiming = { start: number; end: number };

	function extractWordTimings(transcript: Record<string, unknown> | null): WordTiming[] {
		if (!transcript) return [];
		try {
			const words = (transcript as any)?.results?.channels?.[0]?.alternatives?.[0]?.words;
			if (!Array.isArray(words)) return [];
			return words.map((w: any) => ({ start: Number(w.start), end: Number(w.end) }));
		} catch {
			return [];
		}
	}

	const wordTimings = extractWordTimings(article.target_body_transcript);

	// -------------------------------------------------------------------------
	// Reactive state
	// -------------------------------------------------------------------------

	let audioEl: HTMLAudioElement | null = $state(null);
	let activeSentenceIdx = $state(-1);
	let selectedWord: WordToken | null = $state(null);
	let translatedText: string | null = $state(null);
	let translating = $state(false);

	// -------------------------------------------------------------------------
	// Audio: sentence highlighting
	// -------------------------------------------------------------------------

	function handleTimeUpdate() {
		if (!audioEl || wordTimings.length === 0) return;
		const t = audioEl.currentTime;

		// Find the word index at current playback time
		let wordIdx = -1;
		for (let i = 0; i < wordTimings.length; i++) {
			if (wordTimings[i].start <= t && t <= wordTimings[i].end) {
				wordIdx = i;
				break;
			}
			// Between words: use the most recently started word
			if (wordTimings[i].start > t) {
				wordIdx = i - 1;
				break;
			}
		}
		if (wordIdx < 0) return;

		for (const s of allSentences) {
			if (s.startWordIdx <= wordIdx && wordIdx <= s.endWordIdx) {
				activeSentenceIdx = s.idx;
				return;
			}
		}
	}

	// -------------------------------------------------------------------------
	// Word click: fetch translation
	// -------------------------------------------------------------------------

	async function handleWordClick(token: WordToken) {
		selectedWord = token;
		translatedText = null;
		translating = true;

		try {
			const params = new URLSearchParams({
				text: token.text,
				target_language: article.source_language
			});
			const res = await fetch(`/app/translate?${params}`);
			if (res.ok) {
				const body = await res.json();
				translatedText = body.translated_text ?? null;
			}
		} catch {
			translatedText = null;
		} finally {
			translating = false;
		}
	}

	function closePanel() {
		selectedWord = null;
		translatedText = null;
	}

	// -------------------------------------------------------------------------
	// Display helpers
	// -------------------------------------------------------------------------

	const languageNames: Record<string, string> = {
		en: 'English',
		fr: 'French',
		es: 'Spanish',
		it: 'Italian',
		de: 'German',
		pt: 'Portuguese',
		ja: 'Japanese',
		zh: 'Chinese',
		ko: 'Korean'
	};

	const targetLang =
		languageNames[article.target_language] ?? article.target_language.toUpperCase();

	const publishedDate = new Intl.DateTimeFormat('en-GB', {
		year: 'numeric',
		month: 'long',
		day: 'numeric'
	}).format(new Date(article.published_at));
</script>

<!-- Reading progress bar (CSS scroll-driven animation) -->
<div class="progress-bar" aria-hidden="true"></div>

<div class="page">
	<nav class="breadcrumb">
		<a href="/app/articles" class="link">← Articles</a>
	</nav>

	<header class="article-header">
		<p class="article-eyebrow label-md">{targetLang} · {publishedDate}</p>
		<h1 class="article-title">{article.target_title}</h1>
	</header>

	<div class="article-layout">
		<!-- Main content: audio + body -->
		<div class="article-main">
			{#if audioUrl}
				<div class="audio-section">
					<audio
						bind:this={audioEl}
						src={audioUrl}
						controls
						ontimeupdate={handleTimeUpdate}
						class="audio-player"
					>
						Your browser does not support the audio element.
					</audio>
				</div>
			{/if}

			<div class="article-body" lang={article.target_language}>
				{#each paragraphs as para}
					<p class="paragraph">
						{#each para.sentences as sentence}<span
								class="sentence"
								class:sentence--active={activeSentenceIdx === sentence.idx}
								>{#each sentence.tokens as token}{#if token.type === 'word'}<button
											class="word"
											class:word--selected={selectedWord?.wordIdx === token.wordIdx}
											onclick={() => handleWordClick(token)}>{token.text}</button
										>{:else}{token.text}{/if}{/each}</span
							>{/each}
					</p>
				{/each}
			</div>
		</div>

		<!-- Translation panel (desktop: sticky sidebar; mobile: bottom drawer) -->
		<aside
			class="translation-panel"
			class:is-open={selectedWord !== null}
			aria-label="Word translation"
		>
			{#if selectedWord}
				<div class="panel-header">
					<p class="panel-word">{selectedWord.text}</p>
					<button class="btn btn-ghost panel-close" onclick={closePanel} aria-label="Close panel">
						✕
					</button>
				</div>

				{#if translating}
					<div class="panel-loading">
						<div class="spinner" aria-hidden="true"></div>
						<span>Translating…</span>
					</div>
				{:else if translatedText}
					<p class="panel-translation">{translatedText}</p>
					<button class="btn btn-secondary panel-save" disabled aria-disabled="true">
						Add to flashcard
					</button>
				{:else}
					<p class="panel-error">Could not load translation.</p>
				{/if}
			{:else}
				<p class="panel-hint">Tap any word for a translation</p>
			{/if}
		</aside>
	</div>
</div>

<!-- Mobile backdrop: closes the drawer when tapped outside -->
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
<div
	class="drawer-backdrop"
	class:is-visible={selectedWord !== null}
	onclick={closePanel}
	aria-hidden="true"
></div>

<style>
	/* --- Reading progress bar (CSS scroll-driven animation) --- */
	/* Sits at the bottom edge of the sticky topnav (3.25rem) */
	.progress-bar {
		position: fixed;
		top: 3.25rem;
		left: 0;
		right: 0;
		height: 2px;
		background: var(--color-primary);
		transform-origin: left;
		transform: scaleX(0);
		animation: reading-progress linear both;
		animation-timeline: scroll(root);
		z-index: 99;
		pointer-events: none;
	}

	@keyframes reading-progress {
		from {
			transform: scaleX(0);
		}
		to {
			transform: scaleX(1);
		}
	}

	/* --- Page shell --- */
	.page {
		padding: var(--space-8) var(--space-6);
		max-width: 82rem;
		margin: 0 auto;
		display: flex;
		flex-direction: column;
		gap: var(--space-5);
	}

	/* --- Breadcrumb --- */
	.breadcrumb {
		font-family: var(--font-label);
		font-size: var(--text-label-lg);
	}

	/* --- Header --- */
	.article-header {
		display: flex;
		flex-direction: column;
		gap: var(--space-2);
		padding-top: var(--space-2);
	}

	.article-eyebrow {
		color: var(--color-on-surface-variant);
	}

	.article-title {
		font-family: var(--font-display);
		font-size: var(--text-display-md);
		font-weight: var(--weight-bold);
		line-height: var(--leading-tight);
		letter-spacing: var(--tracking-tight);
		color: var(--color-on-surface);
		max-width: 38rem;
	}

	/* --- Two-column layout --- */
	.article-layout {
		display: grid;
		grid-template-columns: 1fr;
		gap: var(--space-6);
		align-items: start;
	}

	@media (min-width: 768px) {
		.article-layout {
			grid-template-columns: 1fr 22rem;
		}
	}

	/* --- Audio --- */
	.audio-section {
		margin-bottom: var(--space-5);
	}

	.audio-player {
		width: 100%;
		accent-color: var(--color-primary);
	}

	/* --- Article body --- */
	.article-body {
		display: flex;
		flex-direction: column;
		gap: var(--space-4);
	}

	.paragraph {
		font-family: var(--font-body);
		font-size: var(--text-body-xl);
		line-height: 2;
		color: var(--color-on-surface);
	}

	/* Sentence: highlighted when audio is at that point */
	.sentence {
		border-radius: var(--radius-xs);
		transition: background-color var(--duration-normal) var(--ease-standard);
	}

	.sentence--active {
		background-color: var(--color-primary-container);
	}

	/* --- Word buttons --- */
	.word {
		display: inline;
		background: none;
		border: none;
		padding: 0 0.05em;
		margin: 0;
		font: inherit;
		color: inherit;
		cursor: pointer;
		border-radius: var(--radius-xs);
		transition:
			background-color var(--duration-fast) var(--ease-standard),
			color var(--duration-fast) var(--ease-standard);
	}

	.word:hover {
		background-color: color-mix(in srgb, var(--color-primary) 12%, transparent);
		color: var(--color-primary);
	}

	.word--selected {
		background-color: color-mix(in srgb, var(--color-primary) 20%, transparent);
		color: var(--color-primary);
		font-weight: var(--weight-medium);
	}

	/* --- Translation panel: Desktop (sticky sidebar) --- */
	@media (min-width: 768px) {
		.translation-panel {
			position: sticky;
			top: var(--space-6);
			background-color: var(--color-surface-container-lowest);
			border-radius: var(--radius-xl);
			padding: var(--space-5);
			min-height: 16rem;
			box-shadow: var(--shadow-tonal-sm);
		}

		.drawer-backdrop {
			display: none;
		}
	}

	/* --- Translation panel: Mobile (bottom drawer) --- */
	@media (max-width: 767px) {
		.translation-panel {
			position: fixed;
			bottom: 0;
			left: 0;
			right: 0;
			z-index: 300;
			background-color: var(--color-surface-container-lowest);
			border-radius: var(--radius-xl) var(--radius-xl) 0 0;
			padding: var(--space-5) var(--space-5) calc(var(--space-5) + env(safe-area-inset-bottom));
			max-height: 55vh;
			overflow-y: auto;
			transform: translateY(100%);
			transition: transform var(--duration-slow) var(--ease-standard);
			box-shadow: 0 -8px 32px color-mix(in srgb, var(--color-on-surface) 8%, transparent);
		}

		.translation-panel.is-open {
			transform: translateY(0);
		}

		.drawer-backdrop {
			position: fixed;
			inset: 0;
			z-index: 200;
			background: color-mix(in srgb, var(--color-on-surface) 20%, transparent);
			opacity: 0;
			pointer-events: none;
			transition: opacity var(--duration-slow) var(--ease-standard);
		}

		.drawer-backdrop.is-visible {
			opacity: 1;
			pointer-events: auto;
		}
	}

	/* --- Panel internals --- */
	.panel-header {
		display: flex;
		align-items: flex-start;
		justify-content: space-between;
		gap: var(--space-2);
		margin-bottom: var(--space-4);
	}

	.panel-word {
		font-family: var(--font-display);
		font-size: var(--text-headline-md);
		font-weight: var(--weight-semibold);
		line-height: var(--leading-snug);
		color: var(--color-on-surface);
	}

	.panel-close {
		flex-shrink: 0;
		color: var(--color-on-surface-variant);
		font-size: var(--text-body-lg);
		line-height: 1;
	}

	.panel-translation {
		font-family: var(--font-body);
		font-size: var(--text-body-lg);
		line-height: var(--leading-relaxed);
		color: var(--color-on-surface-variant);
		font-style: italic;
		margin-bottom: var(--space-4);
	}

	.panel-save {
		width: 100%;
		padding-block: var(--space-2);
		opacity: 0.6;
	}

	.panel-hint {
		font-family: var(--font-label);
		font-size: var(--text-body-sm);
		color: var(--color-on-surface-variant);
		text-align: center;
		padding: var(--space-4) 0;
	}

	.panel-error {
		font-family: var(--font-label);
		font-size: var(--text-body-sm);
		color: var(--color-on-surface-variant);
	}

	.panel-loading {
		display: flex;
		align-items: center;
		gap: var(--space-3);
		font-family: var(--font-label);
		font-size: var(--text-body-sm);
		color: var(--color-on-surface-variant);
	}

	.spinner {
		flex-shrink: 0;
		width: 1rem;
		height: 1rem;
		border: 2px solid var(--color-outline-variant);
		border-top-color: var(--color-primary);
		border-radius: 50%;
		animation: spin 0.9s linear infinite;
	}

	@keyframes spin {
		to {
			transform: rotate(360deg);
		}
	}

	/* --- Responsive --- */
	@media (max-width: 640px) {
		.page {
			padding: var(--space-6) var(--space-4);
		}

		.article-title {
			font-size: var(--text-headline-lg);
		}
	}
</style>
