<script lang="ts">
	import { untrack } from 'svelte';
	import { invalidateAll } from '$app/navigation';
	import type { PageData } from './$types';
	import type { AudioJob, AudioCacheEntry } from '$lib/server/pocketbase';
	import * as m from '$lib/paraglide/messages.js';

	let { data }: { data: PageData } = $props();

	let entries = $state<AudioCacheEntry[]>(untrack(() => data.entries));
	let jobs = $state<AudioJob[]>(untrack(() => data.jobs));

	// Keep in sync on server reloads
	$effect(() => {
		entries = data.entries;
		jobs = data.jobs;
	});

	// ── Live-poll while any job is in-flight ─────────────────────────────────────
	let hasInFlight = $derived(jobs.some((j) => j.status === 'pending' || j.status === 'generating'));

	$effect(() => {
		if (!hasInFlight) return;
		const id = setInterval(() => {
			invalidateAll();
		}, 3000);
		return () => clearInterval(id);
	});

	// ── Tabs ─────────────────────────────────────────────────────────────────────
	type Tab = 'jobs' | 'cache';
	let activeTab = $state<Tab>('jobs');

	// ── Helpers ──────────────────────────────────────────────────────────────────
	function jobStatusColor(status: string) {
		if (status === 'done') return 'text-green-400';
		if (status === 'generating') return 'text-(--color-brand) animate-pulse';
		if (status === 'pending') return 'text-sky-400 animate-pulse';
		if (status === 'failed') return 'text-(--color-danger)';
		return 'text-(--color-text)';
	}

	function fmtDate(s: string) {
		if (!s) return '—';
		return new Date(s).toLocaleString(undefined, {
			month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'
		});
	}

	function duration(started: string, finished: string) {
		if (!started || !finished) return '—';
		const ms = new Date(finished).getTime() - new Date(started).getTime();
		if (ms < 0) return '—';
		const s = Math.floor(ms / 1000);
		if (s < 60) return `${s}s`;
		const m = Math.floor(s / 60);
		return `${m}m ${s % 60}s`;
	}

	function engineLabel(voice: string): string {
		if (voice.startsWith('cfai:')) return 'CF AI';
		if (!voice.includes('_')) return 'Pocket TTS';
		return 'Kokoro';
	}

	// ── Audio jobs stats + filter ────────────────────────────────────────────────
	let jobsQ = $state('');
	let filteredJobs = $derived(
		jobsQ.trim()
			? jobs.filter(
					(j) =>
						j.slug.toLowerCase().includes(jobsQ.toLowerCase().trim()) ||
						j.voice.toLowerCase().includes(jobsQ.toLowerCase().trim()) ||
						j.status.toLowerCase().includes(jobsQ.toLowerCase().trim())
				)
			: jobs
	);

	let stats = $derived({
		total: jobs.length,
		done: jobs.filter((j) => j.status === 'done').length,
		failed: jobs.filter((j) => j.status === 'failed').length,
		inFlight: jobs.filter((j) => j.status === 'pending' || j.status === 'generating').length
	});

	// ── Audio cache filter ───────────────────────────────────────────────────────
	function parseCacheKey(key: string) {
		const parts = key.split('/');
		if (parts.length >= 3) {
			return { slug: parts[0], chapter: parts[1], voice: parts.slice(2).join('/') };
		}
		return { slug: key, chapter: '—', voice: '—' };
	}

	let cacheQ = $state('');
	let filteredCache = $derived(
		cacheQ.trim()
			? entries.filter((e) => e.cache_key.toLowerCase().includes(cacheQ.toLowerCase().trim()))
			: entries
	);
</script>

<svelte:head>
	<title>{m.admin_audio_page_title()}</title>
</svelte:head>

<div class="space-y-6">
	<!-- Header -->
	<div>
		<h1 class="text-2xl font-bold text-(--color-text)">{m.admin_audio_heading()}</h1>
		<p class="text-(--color-muted) text-sm mt-1">
			{stats.total} job{stats.total !== 1 ? 's' : ''} &middot;
			<span class="text-green-400">{stats.done} done</span>
			{#if stats.failed > 0}
				&middot; <span class="text-(--color-danger)">{stats.failed} failed</span>
			{/if}
			{#if stats.inFlight > 0}
				&middot; <span class="text-(--color-brand) animate-pulse">{stats.inFlight} in-flight</span>
			{/if}
			&middot; {entries.length} cached file{entries.length !== 1 ? 's' : ''}
		</p>
	</div>

	<!-- Tabs -->
	<div class="flex gap-1 bg-(--color-surface-2) rounded-lg p-1 w-fit border border-(--color-border)">
		<button
			onclick={() => (activeTab = 'jobs')}
			class="px-4 py-1.5 rounded-md text-sm font-medium transition-colors
				{activeTab === 'jobs' ? 'bg-(--color-surface-3) text-(--color-text)' : 'text-(--color-muted) hover:text-(--color-text)'}"
		>
			Jobs
			{#if stats.inFlight > 0}
				<span class="ml-1.5 inline-flex items-center justify-center w-4 h-4 rounded-full bg-(--color-brand) text-(--color-surface) text-[10px] font-bold">
					{stats.inFlight}
				</span>
			{/if}
		</button>
		<button
			onclick={() => (activeTab = 'cache')}
			class="px-4 py-1.5 rounded-md text-sm font-medium transition-colors
				{activeTab === 'cache' ? 'bg-(--color-surface-3) text-(--color-text)' : 'text-(--color-muted) hover:text-(--color-text)'}"
		>
			Cache
		</button>
	</div>

	<!-- ── Audio Jobs tab ─────────────────────────────────────────────────────── -->
	{#if activeTab === 'jobs'}
		<input
			type="search"
			bind:value={jobsQ}
			placeholder={m.admin_audio_filter_jobs()}
			class="w-full max-w-sm bg-(--color-surface-2) border border-(--color-border) rounded-lg px-3 py-2 text-(--color-text) text-sm placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-(--color-brand)"
		/>

		{#if filteredJobs.length === 0}
			<p class="text-(--color-muted) text-sm py-8 text-center">
				{jobsQ.trim() ? m.admin_audio_no_matching_jobs() : m.admin_audio_no_jobs()}
			</p>
		{:else}
			<!-- Desktop table -->
			<div class="hidden sm:block overflow-x-auto rounded-xl border border-(--color-border)">
				<table class="w-full text-sm">
					<thead class="bg-(--color-surface-2) text-(--color-muted) text-xs uppercase tracking-wide">
						<tr>
							<th class="px-4 py-3 text-left">Book</th>
							<th class="px-4 py-3 text-right">Ch.</th>
							<th class="px-4 py-3 text-left">Voice</th>
							<th class="px-4 py-3 text-left">Engine</th>
							<th class="px-4 py-3 text-left">Status</th>
							<th class="px-4 py-3 text-left">Started</th>
							<th class="px-4 py-3 text-left">Duration</th>
						</tr>
					</thead>
					<tbody class="divide-y divide-(--color-border)/50">
						{#each filteredJobs as job}
							<tr class="bg-(--color-surface) hover:bg-(--color-surface-2)/50 transition-colors">
								<td class="px-4 py-3 text-(--color-text) font-medium">
									<a href="/books/{job.slug}" class="hover:text-(--color-brand) transition-colors">{job.slug}</a>
								</td>
								<td class="px-4 py-3 text-right text-(--color-muted)">{job.chapter}</td>
								<td class="px-4 py-3 text-(--color-muted) font-mono text-xs">{job.voice}</td>
								<td class="px-4 py-3 text-(--color-muted) text-xs">{engineLabel(job.voice)}</td>
								<td class="px-4 py-3">
									<span class="font-medium {jobStatusColor(job.status)}">{job.status}</span>
								</td>
								<td class="px-4 py-3 text-(--color-muted) whitespace-nowrap">{fmtDate(job.started)}</td>
								<td class="px-4 py-3 text-(--color-muted) whitespace-nowrap">{duration(job.started, job.finished)}</td>
							</tr>
							{#if job.error_message}
								<tr class="bg-(--color-danger)/10">
									<td colspan="7" class="px-4 py-2 text-xs text-(--color-danger) font-mono">{job.error_message}</td>
								</tr>
							{/if}
						{/each}
					</tbody>
				</table>
			</div>

			<!-- Mobile cards -->
			<div class="sm:hidden space-y-3">
				{#each filteredJobs as job}
					<div class="bg-(--color-surface) rounded-xl border border-(--color-border) p-4 space-y-2">
						<div class="flex items-start justify-between gap-2">
							<a href="/books/{job.slug}" class="text-(--color-text) font-medium hover:text-(--color-brand) transition-colors truncate">
								{job.slug}
							</a>
							<span class="shrink-0 text-xs font-semibold {jobStatusColor(job.status)}">{job.status}</span>
						</div>
						<div class="grid grid-cols-2 gap-1 text-xs">
							<span class="text-(--color-muted)">Chapter</span><span class="text-(--color-muted) text-right">{job.chapter}</span>
							<span class="text-(--color-muted)">Voice</span><span class="text-(--color-muted) font-mono text-right truncate">{job.voice}</span>
							<span class="text-(--color-muted)">Engine</span><span class="text-(--color-muted) text-right">{engineLabel(job.voice)}</span>
							<span class="text-(--color-muted)">Started</span><span class="text-(--color-muted) text-right">{fmtDate(job.started)}</span>
							<span class="text-(--color-muted)">Duration</span><span class="text-(--color-muted) text-right">{duration(job.started, job.finished)}</span>
						</div>
						{#if job.error_message}
							<p class="text-xs text-(--color-danger) font-mono break-all">{job.error_message}</p>
						{/if}
					</div>
				{/each}
			</div>
		{/if}
	{/if}

	<!-- ── Audio Cache tab ───────────────────────────────────────────────────── -->
	{#if activeTab === 'cache'}
		<input
			type="search"
			bind:value={cacheQ}
			placeholder={m.admin_audio_filter_cache()}
			class="w-full max-w-sm bg-(--color-surface-2) border border-(--color-border) rounded-lg px-3 py-2 text-(--color-text) text-sm placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-(--color-brand)"
		/>

		{#if filteredCache.length === 0}
			<p class="text-(--color-muted) text-sm py-8 text-center">
				{cacheQ.trim() ? m.admin_audio_no_cache_results() : m.admin_audio_cache_empty()}
			</p>
		{:else}
			<!-- Desktop table -->
			<div class="hidden sm:block overflow-x-auto rounded-xl border border-(--color-border)">
				<table class="w-full text-sm">
					<thead class="bg-(--color-surface-2) text-(--color-muted) text-xs uppercase tracking-wide">
						<tr>
							<th class="px-4 py-3 text-left">Book</th>
							<th class="px-4 py-3 text-left">Chapter</th>
							<th class="px-4 py-3 text-left">Voice</th>
							<th class="px-4 py-3 text-left">Engine</th>
							<th class="px-4 py-3 text-left">Filename</th>
							<th class="px-4 py-3 text-left">Updated</th>
						</tr>
					</thead>
					<tbody class="divide-y divide-(--color-border)/50">
						{#each filteredCache as entry}
							{@const parts = parseCacheKey(entry.cache_key)}
							<tr class="bg-(--color-surface) hover:bg-(--color-surface-2)/50 transition-colors">
								<td class="px-4 py-3 text-(--color-text) font-medium">
									<a href="/books/{parts.slug}" class="hover:text-(--color-brand) transition-colors">{parts.slug}</a>
								</td>
								<td class="px-4 py-3 text-(--color-muted)">{parts.chapter}</td>
								<td class="px-4 py-3 text-(--color-muted) font-mono text-xs">{parts.voice}</td>
								<td class="px-4 py-3 text-(--color-muted) text-xs">{engineLabel(parts.voice)}</td>
								<td class="px-4 py-3 text-(--color-muted) font-mono text-xs truncate max-w-[14rem]" title={entry.filename}>
									{entry.filename}
								</td>
								<td class="px-4 py-3 text-(--color-muted) whitespace-nowrap">{fmtDate(entry.updated)}</td>
							</tr>
						{/each}
					</tbody>
				</table>
			</div>

			<!-- Mobile cards -->
			<div class="sm:hidden space-y-3">
				{#each filteredCache as entry}
					{@const parts = parseCacheKey(entry.cache_key)}
					<div class="bg-(--color-surface) rounded-xl border border-(--color-border) p-4 space-y-2">
						<a href="/books/{parts.slug}" class="text-(--color-text) font-medium hover:text-(--color-brand) transition-colors block truncate">
							{parts.slug}
						</a>
					<div class="grid grid-cols-2 gap-1 text-xs">
						<span class="text-(--color-muted)">Chapter</span><span class="text-(--color-muted) text-right">{parts.chapter}</span>
						<span class="text-(--color-muted)">Voice</span><span class="text-(--color-muted) font-mono text-right truncate">{parts.voice}</span>
						<span class="text-(--color-muted)">Engine</span><span class="text-(--color-muted) text-right">{engineLabel(parts.voice)}</span>
						<span class="text-(--color-muted)">Updated</span><span class="text-(--color-muted) text-right">{fmtDate(entry.updated)}</span>
					</div>
						{#if entry.filename}
							<p class="text-xs text-(--color-muted) font-mono truncate" title={entry.filename}>{entry.filename}</p>
						{/if}
					</div>
				{/each}
			</div>
		{/if}
	{/if}
</div>
