<script lang="ts">
	import '../app.css';
	import { onMount, onDestroy } from 'svelte';
	import { browser } from '$app/environment';
	import { page } from '$app/stores';
	import { accent } from '$lib/accent.js';
	import type { ModelStatus } from '$lib/types.js';

	let { children } = $props();

	// Initialize accent (triggers subscriber which sets CSS vars)
	// The store subscriber handles everything; just subscribing here keeps it alive.
	$effect(() => { void $accent; });

	// ── Model status ───────────────────────────────────────
	let modelStatus = $state<ModelStatus>({ state: 'unloaded' });
	let modelEs: EventSource | null = null;

	function refreshModelStatus() {
		fetch('/api/model/status')
			.then((r) => r.json())
			.then((s) => (modelStatus = s as ModelStatus))
			.catch(() => {});
	}

	function subscribeModelEvents() {
		modelEs?.close();
		modelEs = new EventSource('/api/model/events');
		modelEs.addEventListener('model_loading',        () => refreshModelStatus());
		modelEs.addEventListener('model_ready',          () => refreshModelStatus());
		modelEs.addEventListener('model_unloaded',       () => refreshModelStatus());
		modelEs.addEventListener('model_waiting_for_gpu',() => refreshModelStatus());
		modelEs.onerror = () => { /* browser reconnects automatically */ };
	}

	const modelStateMeta: Record<string, { dot: string; label: string; pulse: boolean }> = {
		unloaded:        { dot: 'var(--text-dim)',    label: 'model unloaded',  pulse: false },
		loading:         { dot: '#f0b429',            label: 'model loading…',  pulse: true  },
		waiting_for_gpu: { dot: '#f97316',            label: 'waiting for GPU', pulse: true  },
		ready:           { dot: '#5dd47a',            label: 'whisper-large-v3',pulse: false }
	};

	const modelMeta = $derived(
		modelStateMeta[modelStatus.state] ?? modelStateMeta.unloaded
	);

	// Push notification setup
	onMount(async () => {
		refreshModelStatus();
		subscribeModelEvents();

		if (!browser || !('serviceWorker' in navigator) || !('PushManager' in window)) return;
		try {
			const reg = await navigator.serviceWorker.ready;
			const res = await fetch('/api/push');
			if (!res.ok) return;
			const { publicKey } = await res.json();
			const existing = await reg.pushManager.getSubscription();
			const sub =
				existing ??
				(await reg.pushManager.subscribe({
					userVisibleOnly: true,
					applicationServerKey: urlBase64ToUint8Array(publicKey).buffer as ArrayBuffer
				}));
			await fetch('/api/push', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					endpoint: sub.endpoint,
					keys: {
						p256dh: arrayBufferToBase64(sub.getKey('p256dh')!),
						auth: arrayBufferToBase64(sub.getKey('auth')!)
					}
				})
			});
		} catch (err) {
			console.warn('[push] setup failed:', err);
		}
	});

	onDestroy(() => modelEs?.close());

	function urlBase64ToUint8Array(base64: string): Uint8Array {
		const pad = '='.repeat((4 - (base64.length % 4)) % 4);
		const b64 = (base64 + pad).replace(/-/g, '+').replace(/_/g, '/');
		const raw = atob(b64);
		return Uint8Array.from(raw, (c) => c.charCodeAt(0));
	}
	function arrayBufferToBase64(buf: ArrayBuffer): string {
		return btoa(String.fromCharCode(...new Uint8Array(buf)));
	}

	// Derive active nav item from current path
	const active = $derived.by(() => {
		const path = $page.url.pathname;
		if (path.startsWith('/jobs')) return 'queue';
		if (path.startsWith('/settings')) return 'settings';
		return 'home';
	});

	const navItems = [
		{
			k: 'home',
			label: 'Home',
			href: '/',
			icon: 'M2 7L8 2.5L14 7V13.5H10V9.5H6V13.5H2V7Z'
		},
		{
			k: 'queue',
			label: 'Queue',
			href: '/jobs',
			icon: 'M2 4H14M2 8H14M2 12H10',
			stroke: true
		},
		{
			k: 'settings',
			label: 'Settings',
			href: '/settings',
			icon: 'M8 5.5A2.5 2.5 0 1 1 8 10.5A2.5 2.5 0 0 1 8 5.5ZM8 1V3M8 13V15M3.5 3.5L4.9 4.9M11.1 11.1L12.5 12.5M1 8H3M13 8H15M3.5 12.5L4.9 11.1M11.1 4.9L12.5 3.5',
			stroke: true
		}
	];
</script>

<svelte:head>
	<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
	<link rel="manifest" href="/manifest.json" />
</svelte:head>

<div class="app-shell">
	<!-- ── Rail nav (desktop) ─────────────────────────────── -->
	<nav class="rail" aria-label="Main navigation">
		<!-- Logo -->
		<a href="/" class="logo" aria-label="Tonemark home">
			<div class="logo-icon">
				<svg width="16" height="16" viewBox="0 0 32 32" fill="none" aria-hidden="true">
					<rect x="6" y="8" width="4" height="16" rx="2" fill="#0c0d10" />
					<rect x="14" y="12" width="4" height="8" rx="2" fill="#0c0d10" />
					<rect x="22" y="6" width="4" height="20" rx="2" fill="#0c0d10" />
				</svg>
			</div>
			<span class="logo-name">Tonemark<span class="logo-dot">·</span></span>
		</a>

		<!-- Nav items -->
		<div class="nav-items">
			{#each navItems as item}
				<a
					href={item.href}
					class="nav-item"
					class:active={active === item.k}
					aria-current={active === item.k ? 'page' : undefined}
				>
					{#if active === item.k}
						<div class="nav-indicator" aria-hidden="true"></div>
					{/if}
					<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
						<path
							d={item.icon}
							fill={item.stroke ? 'none' : 'currentColor'}
							stroke={item.stroke ? 'currentColor' : 'none'}
							stroke-width="1.4"
							stroke-linecap="round"
							stroke-linejoin="round"
						/>
					</svg>
					{item.label}
				</a>
			{/each}
		</div>

		<div class="rail-spacer"></div>

		<!-- Status dot -->
		<div class="status-pill">
			<div
				class="status-dot"
				class:pulse={modelMeta.pulse}
				style="background: {modelMeta.dot}"
			></div>
			<span>{modelMeta.label}</span>
		</div>
	</nav>

	<!-- ── Main content ───────────────────────────────────── -->
	<main class="main-content">
		{@render children()}
	</main>

	<!-- ── Tab bar (mobile) ───────────────────────────────── -->
	<nav class="tabbar" aria-label="Mobile navigation">
		{#each navItems as item}
			<a href={item.href} class="tab-item" class:active={active === item.k}>
				<svg width="22" height="22" viewBox="0 0 16 16" aria-hidden="true">
					<path
						d={item.icon}
						fill={item.stroke ? 'none' : 'currentColor'}
						stroke={item.stroke ? 'currentColor' : 'none'}
						stroke-width="1.4"
						stroke-linecap="round"
						stroke-linejoin="round"
					/>
				</svg>
				<span>{item.label}</span>
			</a>
		{/each}
	</nav>
</div>

<style>
	.app-shell {
		display: flex;
		min-height: 100vh;
		min-height: 100dvh;
	}

	/* ── Rail ─────────────────────────────────────────────── */
	.rail {
		width: var(--rail-width);
		flex-shrink: 0;
		border-right: 1px solid var(--border);
		display: flex;
		flex-direction: column;
		padding: 24px 14px;
		background: rgba(255, 255, 255, 0.015);
		position: sticky;
		top: 0;
		height: 100vh;
		overflow-y: auto;
	}

	.logo {
		display: flex;
		align-items: center;
		gap: 10px;
		padding: 0 8px 28px;
		text-decoration: none;
		color: var(--text);
	}
	.logo-icon {
		width: 28px;
		height: 28px;
		border-radius: 8px;
		background: var(--accent);
		display: flex;
		align-items: center;
		justify-content: center;
		flex-shrink: 0;
	}
	.logo-name {
		font-size: 15px;
		font-weight: 600;
		letter-spacing: -0.02em;
	}
	.logo-dot {
		color: rgba(232, 233, 236, 0.4);
		font-weight: 400;
	}

	.nav-items {
		display: flex;
		flex-direction: column;
		gap: 2px;
	}
	.nav-item {
		display: flex;
		align-items: center;
		gap: 12px;
		padding: 9px 12px;
		border-radius: 8px;
		font-size: 13.5px;
		font-weight: 500;
		color: var(--text-muted);
		text-decoration: none;
		position: relative;
		transition: background 0.15s, color 0.15s;
	}
	.nav-item:hover {
		background: rgba(255, 255, 255, 0.04);
		color: var(--text);
	}
	.nav-item.active {
		background: rgba(255, 255, 255, 0.06);
		color: #fff;
	}
	.nav-indicator {
		position: absolute;
		left: -14px;
		top: 8px;
		bottom: 8px;
		width: 2px;
		border-radius: 2px;
		background: var(--accent);
	}

	.rail-spacer { flex: 1; }

	.status-pill {
		display: flex;
		align-items: center;
		gap: 8px;
		padding: 10px 14px;
		border-radius: 10px;
		border: 1px solid var(--border);
		font-size: 11px;
		font-family: var(--font-mono);
		color: var(--text-muted);
	}
	.status-dot {
		width: 6px;
		height: 6px;
		border-radius: 3px;
		flex-shrink: 0;
		transition: background 0.4s;
	}
	.status-dot.pulse {
		animation: dot-pulse 1.4s ease-in-out infinite;
	}
	@keyframes dot-pulse {
		0%, 100% { opacity: 1; }
		50%       { opacity: 0.3; }
	}

	/* ── Main content ─────────────────────────────────────── */
	.main-content {
		flex: 1;
		min-width: 0;
		overflow-y: auto;
	}

	/* ── Tab bar (mobile only) ────────────────────────────── */
	.tabbar {
		display: none;
	}

	/* ── Responsive ───────────────────────────────────────── */
	@media (max-width: 768px) {
		.rail {
			display: none;
		}
		.app-shell {
			flex-direction: column;
			padding-bottom: 72px;
		}
		.tabbar {
			display: flex;
			position: fixed;
			bottom: 0;
			left: 0;
			right: 0;
			border-top: 1px solid var(--border);
			background: rgba(12, 13, 16, 0.9);
			backdrop-filter: blur(20px);
			padding: 10px 0 max(30px, env(safe-area-inset-bottom));
			justify-content: space-around;
			z-index: 100;
		}
		.tab-item {
			display: flex;
			flex-direction: column;
			align-items: center;
			gap: 4px;
			color: rgba(232, 233, 236, 0.4);
			text-decoration: none;
			transition: color 0.15s;
		}
		.tab-item.active {
			color: var(--accent);
		}
		.tab-item span {
			font-size: 9.5px;
			font-family: var(--font-mono);
			text-transform: uppercase;
			letter-spacing: 0.06em;
		}
	}
</style>

