<script lang="ts">
	import { resolve } from '$app/paths';
	import type { PartsOfSpeechData } from '$lib/spacy/types';
	import type { PageProps } from './$types';
	import TargetLanguageBody from './TargetLanguageBody.svelte';
	import type { Paragraph, Sentence, SentenceToken, Transcript } from './Transcript';
	import TranslationPanel from './TranslationPanel.svelte';
	import { translateText } from './translate.remote';

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

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

	function extractParagraphsAndWordCount(text: PartsOfSpeechData): {
		paragraphs: Paragraph[];
		totalWords: number;
	} {
		const paragraphs: Paragraph[] = [{ index: 0, sentences: [] }];
		let wordIdx = 0;
		let sentenceIdx = 0;

		text.sentences.forEach((s) => {
			const sentence: Sentence = {
				idx: sentenceIdx++,
				text: s.text,
				startWordIdx: wordIdx,
				endWordIdx: wordIdx + s.tokens.length - 1,
				tokens: s.tokens.map((t) => ({
					...t,
					idx: wordIdx++
				})) as SentenceToken[]
			};

			const sentenceEndsWithNewLine = s.text.endsWith('\n');
			if (sentenceEndsWithNewLine) {
				paragraphs.push({ index: paragraphs.length, sentences: [] });
			} else {
				paragraphs[paragraphs.length - 1].sentences.push(sentence);
			}
		});

		return { paragraphs, totalWords: wordIdx };
	}

	const { paragraphs } = extractParagraphsAndWordCount(
		article.target_body_pos as Record<string, any> as PartsOfSpeechData
	);

	// 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: Transcript | null): WordTiming[] {
		if (!transcript) return [];
		try {
			const timings: WordTiming[] = [];
			for (const utterance of transcript.utterances) {
				for (const word of utterance.words) {
					timings.push({ start: word.start, end: word.end });
				}
			}
			return timings;
		} catch {
			return [];
		}
	}

	const wordTimings = extractWordTimings(
		article.target_body_transcript as unknown as Transcript | null
	);

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

	let audioEl: HTMLAudioElement | null = $state(null);
	let activeSentenceIdx = $state(-1);
	let selectedSentenceToken: SentenceToken | null = $state(null);
	let selectedSentence: Sentence | 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: SentenceToken, sentence: Sentence) {
		selectedSentenceToken = token;
		activeSentenceIdx = sentence.idx;
		translatedText = null;
		translating = true;

		try {
			const result = await translateText({
				fromLanguage: article.target_language,
				toLanguage: article.source_language,
				sentenceText: sentence.text,
				text: token.text
			});

			translatedText = result.text;
			console.log({ result });
		} catch {
			translatedText = null;
		} finally {
			translating = false;
		}
	}

	function closePanel() {
		selectedSentenceToken = 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={resolve('/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 article.target_audio_url}
				<div class="audio-section">
					<audio
						bind:this={audioEl}
						src={article.target_audio_url}
						controls
						ontimeupdate={handleTimeUpdate}
						class="audio-player"
					>
						Your browser does not support the audio element.
					</audio>
				</div>
			{/if}

			<TargetLanguageBody
				lang={article.source_language}
				{paragraphs}
				{activeSentenceIdx}
				onWordClick={handleWordClick}
				{selectedSentenceToken}
			/>
		</div>

		<TranslationPanel {closePanel} {selectedSentenceToken} {translatedText} {translating} />
	</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={selectedSentenceToken !== 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);
	}

	@media (min-width: 768px) {
		.drawer-backdrop {
			display: none;
		}
	}

	/* --- Translation panel: Mobile (bottom drawer) --- */
	@media (max-width: 767px) {
		.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;
		}
	}

	@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>
