<script lang="ts">
	import { untrack } from 'svelte';
	import { invalidateAll } from '$app/navigation';
	import type { PageData } from './$types';
	import type { AIJob } from '$lib/server/pocketbase';
	import { cn } from '$lib/utils';

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

	let jobs = $state<AIJob[]>(untrack(() => data.jobs));

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

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

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

	// ── Filter / search ───────────────────────────────────────────────────────────
	let query = $state('');
	let statusFilter = $state<string>('all');

	const STATUS_OPTIONS = ['all', 'pending', 'running', 'done', 'failed', 'cancelled'] as const;

	let filteredJobs = $derived(
		jobs.filter((j) => {
			const q = query.trim().toLowerCase();
			const matchesQ =
				!q ||
				j.slug.toLowerCase().includes(q) ||
				j.kind.toLowerCase().includes(q) ||
				j.model.toLowerCase().includes(q) ||
				(j.error_message ?? '').toLowerCase().includes(q);
			const matchesStatus = statusFilter === 'all' || j.status === statusFilter;
			return matchesQ && matchesStatus;
		})
	);

	// ── Stats ────────────────────────────────────────────────────────────────────
	let stats = $derived({
		total: jobs.length,
		running: jobs.filter((j) => j.status === 'running').length,
		pending: jobs.filter((j) => j.status === 'pending').length,
		done: jobs.filter((j) => j.status === 'done').length,
		failed: jobs.filter((j) => j.status === 'failed').length
	});

	// ── Cancel ────────────────────────────────────────────────────────────────────
	let cancellingId = $state<string | null>(null);
	let cancelError = $state('');

	async function cancelJob(id: string) {
		if (cancellingId) return;
		cancellingId = id;
		cancelError = '';
		try {
			const res = await fetch(`/api/admin/ai-jobs/${id}/cancel`, { method: 'POST' });
			if (!res.ok) {
				const body = await res.json().catch(() => ({}));
				cancelError = body.error ?? `Error ${res.status}`;
			} else {
				await invalidateAll();
			}
		} catch {
			cancelError = 'Network error.';
		} finally {
			cancellingId = null;
		}
	}

	// ── Review & Apply (chapter-names jobs) ──────────────────────────────────────

	interface ProposedTitle {
		number: number;
		old_title: string;
		new_title: string;
	}

	interface ReviewState {
		jobId: string;
		slug: string;
		pattern: string;
		titles: ProposedTitle[];
		loading: boolean;
		error: string;
		applying: boolean;
		applyError: string;
		applyDone: boolean;
	}

	let review = $state<ReviewState | null>(null);

	async function openReview(job: AIJob) {
		review = {
			jobId: job.id,
			slug: job.slug,
			pattern: '',
			titles: [],
			loading: true,
			error: '',
			applying: false,
			applyError: '',
			applyDone: false
		};

		try {
			const res = await fetch(`/api/admin/ai-jobs/${job.id}`);
			if (!res.ok) throw new Error(`HTTP ${res.status}`);
			const data = await res.json();

			let payload: { pattern?: string; slug?: string; results?: ProposedTitle[] } = {};
			try {
				payload = JSON.parse(data.payload ?? '{}');
			} catch {
				// ignore
			}

			review.pattern = payload.pattern ?? '';
			review.titles = (payload.results ?? []).map((t: ProposedTitle) => ({ ...t }));
			review.loading = false;
		} catch (e) {
			review.loading = false;
			review.error = String(e);
		}
	}

	function closeReview() {
		review = null;
	}

	async function applyReview() {
		if (!review || review.applying) return;
		review.applying = true;
		review.applyError = '';
		review.applyDone = false;

		const chapters = review.titles.map((t) => ({ number: t.number, title: t.new_title }));
		try {
			const res = await fetch('/api/admin/text-gen/chapter-names/apply', {
				method: 'POST',
				headers: { 'content-type': 'application/json' },
				body: JSON.stringify({ slug: review.slug, chapters })
			});
			if (!res.ok) {
				const body = await res.json().catch(() => ({}));
				review.applyError = body.error ?? `Error ${res.status}`;
			} else {
				review.applyDone = true;
			}
		} catch {
			review.applyError = 'Network error.';
		} finally {
			review.applying = false;
		}
	}

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

	function statusBg(status: string) {
		if (status === 'done') return 'bg-green-400/10 text-green-400';
		if (status === 'running') return 'bg-(--color-brand)/10 text-(--color-brand)';
		if (status === 'pending') return 'bg-sky-400/10 text-sky-400';
		if (status === 'failed') return 'bg-(--color-danger)/10 text-(--color-danger)';
		if (status === 'cancelled') return 'bg-zinc-700/50 text-(--color-muted)';
		return 'bg-zinc-700/50 text-(--color-text)';
	}

	function kindLabel(kind: string) {
		const labels: Record<string, string> = {
			'chapter-names': 'Chapter Names',
			'batch-covers': 'Batch Covers',
			'chapter-covers': 'Chapter Covers',
			'refresh-metadata': 'Refresh Metadata'
		};
		return labels[kind] ?? kind;
	}

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

	function duration(started: string | undefined, finished: string | undefined) {
		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 progress(job: AIJob) {
		if (!job.items_total) return null;
		return Math.round((job.items_done / job.items_total) * 100);
	}
</script>

<div class="max-w-6xl mx-auto space-y-6">
	<!-- Header -->
	<div class="flex items-center justify-between gap-4">
		<div>
			<h1 class="text-2xl font-bold text-(--color-text)">AI Jobs</h1>
			<p class="text-sm text-(--color-muted) mt-0.5">Background AI generation tasks</p>
		</div>
		<button
			onclick={() => invalidateAll()}
			class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
		>
			<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
				<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
					d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
			</svg>
			Refresh
		</button>
	</div>

	<!-- Stats bar -->
	<div class="grid grid-cols-2 sm:grid-cols-5 gap-3">
		{#each [
			{ label: 'Total', value: stats.total, color: 'text-(--color-text)' },
			{ label: 'Running', value: stats.running, color: 'text-(--color-brand)' },
			{ label: 'Pending', value: stats.pending, color: 'text-sky-400' },
			{ label: 'Done', value: stats.done, color: 'text-green-400' },
			{ label: 'Failed', value: stats.failed, color: 'text-(--color-danger)' }
		] as stat}
			<div class="rounded-lg border border-(--color-border) bg-(--color-surface-2) px-4 py-3">
				<p class="text-xs text-(--color-muted) mb-1">{stat.label}</p>
				<p class={cn('text-xl font-bold tabular-nums', stat.color)}>{stat.value}</p>
			</div>
		{/each}
	</div>

	<!-- Filters -->
	<div class="flex flex-wrap gap-3 items-center">
		<input
			type="search"
			placeholder="Search slug, kind, model…"
			bind:value={query}
			class="flex-1 min-w-48 px-3 py-1.5 rounded-md bg-(--color-surface-2) border border-(--color-border) text-sm text-(--color-text) placeholder:text-(--color-muted) focus:outline-none focus:ring-1 focus:ring-(--color-brand)"
		/>
		<div class="flex gap-1 flex-wrap">
			{#each STATUS_OPTIONS as s}
				<button
					onclick={() => (statusFilter = s)}
					class={cn(
						'px-2.5 py-1 rounded-md text-xs font-medium transition-colors capitalize',
						statusFilter === s
							? 'bg-(--color-brand) text-black'
							: 'bg-(--color-surface-2) text-(--color-muted) hover:text-(--color-text)'
					)}
				>
					{s}
				</button>
			{/each}
		</div>
	</div>

	<!-- Cancel error -->
	{#if cancelError}
		<p class="text-sm text-(--color-danger) bg-(--color-danger)/10 rounded-lg px-3 py-2">{cancelError}</p>
	{/if}

	<!-- Jobs table -->
	{#if filteredJobs.length === 0}
		<div class="rounded-lg border border-(--color-border) bg-(--color-surface-2) px-6 py-12 text-center">
			<p class="text-(--color-muted) text-sm">
				{jobs.length === 0 ? 'No AI jobs found.' : 'No jobs match your filters.'}
			</p>
		</div>
	{:else}
		<div class="rounded-lg border border-(--color-border) overflow-hidden">
			<table class="w-full text-sm">
				<thead>
					<tr class="border-b border-(--color-border) bg-(--color-surface-2)">
						<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider">Status</th>
						<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider">Kind</th>
						<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider">Slug</th>
						<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider hidden sm:table-cell">Model</th>
						<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider hidden md:table-cell">Progress</th>
						<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider hidden lg:table-cell">Started</th>
						<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider hidden lg:table-cell">Duration</th>
						<th class="px-4 py-2.5 text-right text-xs font-semibold text-(--color-muted) uppercase tracking-wider">Actions</th>
					</tr>
				</thead>
				<tbody class="divide-y divide-(--color-border)">
					{#each filteredJobs as job (job.id)}
						<tr class="bg-(--color-surface) hover:bg-(--color-surface-2)/40 transition-colors">
							<!-- Status badge -->
							<td class="px-4 py-3">
								<span class={cn('inline-flex items-center px-2 py-0.5 rounded text-xs font-medium', statusBg(job.status))}>
									{job.status}
								</span>
							</td>

							<!-- Kind -->
							<td class="px-4 py-3 font-medium text-(--color-text)">
								{kindLabel(job.kind)}
							</td>

							<!-- Slug -->
							<td class="px-4 py-3 max-w-[12rem]">
								{#if job.slug}
									<a
										href="/admin/image-gen?slug={job.slug}"
										class="text-(--color-brand) hover:underline truncate block font-mono text-xs"
									>
										{job.slug}
									</a>
								{:else}
									<span class="text-(--color-muted) text-xs">—</span>
								{/if}
							</td>

							<!-- Model -->
							<td class="px-4 py-3 hidden sm:table-cell">
								<span class="font-mono text-xs text-(--color-muted) truncate block max-w-[10rem]" title={job.model}>
									{job.model || '—'}
								</span>
							</td>

							<!-- Progress -->
							<td class="px-4 py-3 hidden md:table-cell">
								{#if job.items_total > 0}
									{@const pct = progress(job)}
									<div class="flex items-center gap-2">
										<div class="w-20 h-1.5 rounded-full bg-(--color-border) overflow-hidden">
											<div
												class={cn(
													'h-full rounded-full transition-all',
													job.status === 'done' ? 'bg-green-400' :
													job.status === 'failed' ? 'bg-(--color-danger)' :
													'bg-(--color-brand)'
												)}
												style="width: {pct}%"
											></div>
										</div>
										<span class="text-xs text-(--color-muted) tabular-nums whitespace-nowrap">
											{job.items_done}/{job.items_total}
										</span>
									</div>
								{:else}
									<span class="text-(--color-muted) text-xs">—</span>
								{/if}
							</td>

							<!-- Started -->
							<td class="px-4 py-3 hidden lg:table-cell">
								<span class="text-xs text-(--color-muted) whitespace-nowrap">{fmtDate(job.started)}</span>
							</td>

							<!-- Duration -->
							<td class="px-4 py-3 hidden lg:table-cell">
								<span class="text-xs text-(--color-muted) whitespace-nowrap tabular-nums">
									{duration(job.started, job.finished)}
								</span>
							</td>

							<!-- Actions -->
							<td class="px-4 py-3 text-right">
								<div class="flex items-center justify-end gap-2">
									{#if job.status === 'pending' || job.status === 'running'}
										<button
											onclick={() => cancelJob(job.id)}
											disabled={cancellingId === job.id}
											class="px-2 py-1 rounded text-xs font-medium bg-(--color-danger)/10 text-(--color-danger) hover:bg-(--color-danger)/20 disabled:opacity-50 transition-colors"
										>
											{cancellingId === job.id ? 'Cancelling…' : 'Cancel'}
										</button>
									{/if}
									{#if job.kind === 'chapter-names' && job.status === 'done'}
										<button
											onclick={() => openReview(job)}
											class="px-2 py-1 rounded text-xs font-medium bg-green-400/10 text-green-400 hover:bg-green-400/20 transition-colors"
										>
											Review
										</button>
									{/if}
									{#if job.error_message}
										<span
											class="text-xs text-(--color-danger) max-w-[12rem] truncate"
											title={job.error_message}
										>
											{job.error_message}
										</span>
									{/if}
								</div>
							</td>
						</tr>
					{/each}
				</tbody>
			</table>
		</div>
		<p class="text-xs text-(--color-muted)">
			Showing {filteredJobs.length} of {jobs.length} jobs
		</p>
	{/if}
</div>

<!-- ── Review & Apply panel ──────────────────────────────────────────────────── -->
{#if review}
	<!-- Backdrop -->
	<div
		class="fixed inset-0 z-40 bg-black/60 backdrop-blur-sm"
		onclick={closeReview}
		role="presentation"
	></div>

	<!-- Panel -->
	<div class="fixed inset-x-0 bottom-0 z-50 max-h-[85vh] overflow-hidden flex flex-col rounded-t-2xl bg-(--color-surface) border-t border-(--color-border) shadow-2xl sm:inset-auto sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2 sm:-translate-y-1/2 sm:w-[min(900px,95vw)] sm:max-h-[85vh] sm:rounded-xl sm:border sm:shadow-2xl">
		<!-- Panel header -->
		<div class="flex items-center justify-between gap-4 px-5 py-4 border-b border-(--color-border) shrink-0">
			<div>
				<h2 class="text-base font-semibold text-(--color-text)">Review Chapter Names</h2>
				<p class="text-xs text-(--color-muted) mt-0.5">
					<span class="font-mono">{review.slug}</span>
					{#if review.pattern}
						· pattern: <span class="font-mono">{review.pattern}</span>
					{/if}
				</p>
			</div>
			<button
				onclick={closeReview}
				class="p-1.5 rounded-md text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
				aria-label="Close"
			>
				<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
				</svg>
			</button>
		</div>

		<!-- Body -->
		<div class="flex-1 overflow-y-auto">
			{#if review.loading}
				<div class="flex items-center justify-center py-16 text-(--color-muted) text-sm">
					Loading results…
				</div>
			{:else if review.error}
				<div class="px-5 py-8 text-center">
					<p class="text-(--color-danger) text-sm">{review.error}</p>
				</div>
			{:else if review.titles.length === 0}
				<div class="px-5 py-8 text-center">
					<p class="text-(--color-muted) text-sm">No results found in this job's payload.</p>
				</div>
			{:else}
				<table class="w-full text-sm">
					<thead class="sticky top-0 bg-(--color-surface) border-b border-(--color-border)">
						<tr>
							<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider w-16">#</th>
							<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider w-1/2">Old Title</th>
							<th class="px-4 py-2.5 text-left text-xs font-semibold text-(--color-muted) uppercase tracking-wider">New Title (editable)</th>
						</tr>
					</thead>
					<tbody class="divide-y divide-(--color-border)">
						{#each review.titles as title (title.number)}
							<tr class="bg-(--color-surface) hover:bg-(--color-surface-2)/40 transition-colors">
								<td class="px-4 py-2 text-xs text-(--color-muted) tabular-nums">{title.number}</td>
								<td class="px-4 py-2 text-xs text-(--color-muted) max-w-0">
									<span class="block truncate" title={title.old_title}>{title.old_title || '—'}</span>
								</td>
								<td class="px-4 py-2">
									<input
										type="text"
										bind:value={title.new_title}
										class="w-full px-2 py-1 rounded bg-(--color-surface-2) border border-(--color-border) text-xs text-(--color-text) focus:outline-none focus:ring-1 focus:ring-(--color-brand)"
									/>
								</td>
							</tr>
						{/each}
					</tbody>
				</table>
			{/if}
		</div>

		<!-- Footer -->
		{#if !review.loading && !review.error && review.titles.length > 0}
			<div class="px-5 py-4 border-t border-(--color-border) shrink-0 flex items-center justify-between gap-4">
				<div class="text-xs text-(--color-muted)">
					{review.titles.length} chapters
				</div>
				<div class="flex items-center gap-3">
					{#if review.applyError}
						<p class="text-xs text-(--color-danger)">{review.applyError}</p>
					{/if}
					{#if review.applyDone}
						<p class="text-xs text-green-400">Applied successfully.</p>
					{/if}
					<button
						onclick={closeReview}
						class="px-3 py-1.5 rounded-md text-sm text-(--color-muted) hover:text-(--color-text) hover:bg-(--color-surface-2) transition-colors"
					>
						Close
					</button>
					<button
						onclick={applyReview}
						disabled={review.applying || review.applyDone}
						class="px-4 py-1.5 rounded-md text-sm font-medium bg-(--color-brand) text-black hover:bg-amber-300 disabled:opacity-50 transition-colors"
					>
						{review.applying ? 'Applying…' : review.applyDone ? 'Applied' : 'Apply All'}
					</button>
				</div>
			</div>
		{/if}
	</div>
{/if}
