<script lang="ts">
	import { base } from '$app/paths';
	import { goto } from '$app/navigation';
	import { onMount } from 'svelte';
	import { page } from '$app/state';
	import { api } from '$lib/api';
	import { currentUser, getToken } from '$lib/auth';
	import RoleChip from '$lib/RoleChip.svelte';

	let { children } = $props();

	let booting = $state(true);
	const user = $derived($currentUser);

	const isLoginRoute = $derived(page.url.pathname.endsWith('/login'));

	function loginRedirectTarget(): string {
		const here = `${page.url.pathname}${page.url.search}`;
		if (here === `${base}/login` || here.startsWith(`${base}/login?`)) {
			return `${base}/login`;
		}
		return `${base}/login?returnTo=${encodeURIComponent(here)}`;
	}

	onMount(async () => {
		// Hydrate the session: if there's a token, ask the server who we
		// are. On 401 the fetch wrapper already redirects to /login and
		// clears state; on success we land in the SPA fully signed in.
		const tok = getToken();
		if (!tok) {
			if (!isLoginRoute) {
				await goto(loginRedirectTarget());
			}
			booting = false;
			return;
		}
		try {
			const me = await api.auth.me();
			currentUser.set(me);
		} catch {
			// adminRequest handles 401 redirects. For other errors fall
			// through — the page will surface its own error state.
		}
		booting = false;
	});

	async function handleLogout() {
		await api.auth.logout();
		await goto(`${base}/login`);
	}
</script>

<div class="shell">
	<header>
		<a href={base + '/'} class="brand">PiCloud</a>
		<nav>
			<a href={base + '/apps'}>Apps</a>
			<a href={base + '/groups'}>Groups</a>
			{#if user && user.instance_role !== 'member'}
				<a href={base + '/users'}>Users</a>
			{/if}
		</nav>
		<div class="spacer"></div>
		{#if user}
			<div class="usermenu">
				<a href={base + '/profile'} class="profile-chip" title="View profile">
					<RoleChip role={user.instance_role} size="sm" />
					<span class="username">{user.username}</span>
				</a>
				<button type="button" class="logout" onclick={handleLogout}>Logout</button>
			</div>
		{/if}
	</header>
	<main>
		{#if booting}
			<p class="boot">Loading…</p>
		{:else}
			{@render children?.()}
		{/if}
	</main>
</div>

<style>
	:global(:root) {
		/* Slate-based dark palette — the source of truth for tokens that
		   per-page styles reference via `var(--name, fallback)`. Without
		   these defaults the fallbacks (e.g. #666 muted text on #f5f5f5
		   secondary background) render light-on-dark and bury content
		   on dead-letters, files, users, invitations and queues pages. */
		--bg-primary: #0f172a;
		--bg-secondary: #0b1220;
		--bg-elevated: #1e293b;
		--bg-elevated-hover: #283549;
		--text-primary: #e2e8f0;
		--text-strong: #cbd5e1;
		--text-muted: #94a3b8;
		--text-subtle: #64748b;
		--border: #334155;
		--border-subtle: #1e293b;
		--color-link: #38bdf8;
		--color-muted: #94a3b8;
		--color-danger: #f87171;
		--color-danger-bg: #7f1d1d;
		--color-danger-fg: #fecaca;
		--color-danger-border: #b91c1c;
		--color-success: #34d399;
		--color-success-bg: #14532d;
		--color-success-fg: #6ee7b7;
		--color-warning: #fbbf24;
		--color-warning-bg: #3f2e07;
		--color-warning-fg: #fde68a;
		--color-warning-border: #ca8a04;
		--accent: #38bdf8;
		--accent-fg: #0b1220;

		/* Aliases for orphan references that several pages adopted before
		   the token block was canonicalised (F-U-004). Removing these
		   requires sweeping ~30 call sites; until then the alias keeps
		   the visuals consistent. */
		--muted: var(--text-muted);
		--link: var(--color-link);
		--text: var(--text-primary);
		--color-error: var(--color-danger);
		--color-border: var(--border);
		--chip-bg: var(--bg-elevated);
		--code-bg: var(--bg-secondary);

		/* Radii, shadows, z scale. */
		--radius-sm: 0.25rem;
		--radius-md: 0.375rem;
		--radius-lg: 0.5rem;
		--radius-pill: 999px;
		--shadow-elev-2: 0 25px 50px -12px rgba(0, 0, 0, 0.7);
		--z-popover: 50;
		--z-modal: 100;
		--z-toast: 200;
	}

	/* Global form-control resets — these stop macOS / Linux native
	   controls (selects with OS chevrons, blue-tick checkboxes, raised
	   default buttons, light-rendered date pickers) from breaking the
	   dark theme. Per-page styles still win because of specificity. */
	:global(button) {
		font-family: inherit;
	}
	:global(select) {
		appearance: none;
		-webkit-appearance: none;
		background-color: var(--bg-secondary);
		color: var(--text-primary);
		border: 1px solid var(--border);
		border-radius: var(--radius-md);
		padding: 0.45rem 2rem 0.45rem 0.75rem;
		font: inherit;
		/* Inline-SVG chevron so the dropdown indicator matches the
		   slate-400 muted text colour regardless of OS. */
		background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='none' stroke='%2394a3b8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/></svg>");
		background-repeat: no-repeat;
		background-position: right 0.6rem center;
		cursor: pointer;
	}
	:global(select:focus) {
		outline: none;
		border-color: var(--color-link);
	}
	:global(input[type='checkbox']),
	:global(input[type='radio']) {
		appearance: none;
		-webkit-appearance: none;
		width: 1rem;
		height: 1rem;
		margin: 0;
		background: var(--bg-secondary);
		border: 1px solid var(--border);
		flex-shrink: 0;
		cursor: pointer;
		display: inline-block;
		vertical-align: middle;
		position: relative;
	}
	:global(input[type='checkbox']) {
		border-radius: var(--radius-sm);
	}
	:global(input[type='radio']) {
		border-radius: var(--radius-pill);
	}
	:global(input[type='checkbox']:checked),
	:global(input[type='radio']:checked) {
		background: var(--color-link);
		border-color: var(--color-link);
	}
	:global(input[type='checkbox']:checked::after) {
		content: '';
		position: absolute;
		left: 3px;
		top: 0;
		width: 4px;
		height: 8px;
		border: solid var(--accent-fg);
		border-width: 0 2px 2px 0;
		transform: rotate(45deg);
	}
	:global(input[type='radio']:checked::after) {
		content: '';
		position: absolute;
		left: 3px;
		top: 3px;
		width: 8px;
		height: 8px;
		border-radius: var(--radius-pill);
		background: var(--accent-fg);
	}
	:global(input[type='checkbox']:focus-visible),
	:global(input[type='radio']:focus-visible) {
		outline: 2px solid var(--color-link);
		outline-offset: 2px;
	}
	/* Number input spinners — hidden so the dark theme doesn't ship
	   OS-blue stepper arrows on inputs like timeout/visibility knobs. */
	:global(input[type='number']) {
		-moz-appearance: textfield;
		appearance: textfield;
	}
	:global(input[type='number']::-webkit-outer-spin-button),
	:global(input[type='number']::-webkit-inner-spin-button) {
		-webkit-appearance: none;
		margin: 0;
	}
	/* Date picker icon — recoloured to match muted text. */
	:global(input[type='date']::-webkit-calendar-picker-indicator) {
		filter: invert(0.7);
		cursor: pointer;
	}
	/* <details> default triangle replaced with a slate chevron so the
	   disclosure widget tracks the rest of the dark theme. Audit fix:
	   scoped to `details.chevron` so the chevron doesn't leak into
	   contexts where the summary is doing layout work of its own (e.g.
	   the script exec-list, whose summary has its own status badge,
	   time, and path columns — a chevron there reads as visual noise). */
	:global(details.chevron > summary) {
		list-style: none;
		cursor: pointer;
	}
	:global(details.chevron > summary::-webkit-details-marker) {
		display: none;
	}
	:global(details.chevron > summary::before) {
		content: '▸';
		display: inline-block;
		margin-right: 0.4rem;
		color: var(--text-muted);
		transition: transform 120ms ease;
	}
	:global(details.chevron[open] > summary::before) {
		transform: rotate(90deg);
	}

	:global(html, body) {
		margin: 0;
		font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
		background: var(--bg-primary);
		color: var(--text-primary);
	}

	.shell {
		min-height: 100vh;
		display: flex;
		flex-direction: column;
	}

	header {
		display: flex;
		align-items: center;
		gap: 2rem;
		padding: 1rem 2rem;
		border-bottom: 1px solid #1e293b;
		background: #0b1220;
	}

	.brand {
		font-weight: 700;
		font-size: 1.125rem;
		color: #38bdf8;
		text-decoration: none;
	}

	nav {
		display: flex;
		gap: 1.5rem;
	}

	nav a {
		color: #94a3b8;
		text-decoration: none;
		font-size: 0.9rem;
	}

	nav a:hover {
		color: #e2e8f0;
	}

	.spacer {
		flex: 1;
	}

	.usermenu {
		display: flex;
		align-items: center;
		gap: 0.75rem;
		font-size: 0.875rem;
	}

	.profile-chip {
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
		padding: 0.25rem 0.55rem;
		border-radius: 0.375rem;
		text-decoration: none;
		border: 1px solid transparent;
	}
	.profile-chip:hover {
		background: #1e293b;
		border-color: #334155;
	}

	.username {
		color: #cbd5e1;
	}

	.logout {
		background: transparent;
		color: #94a3b8;
		border: 1px solid #334155;
		padding: 0.35rem 0.75rem;
		border-radius: 0.375rem;
		cursor: pointer;
		font-size: 0.8rem;
	}

	.logout:hover {
		background: #1e293b;
		color: #e2e8f0;
	}

	main {
		flex: 1;
		padding: 2rem;
		max-width: 64rem;
		width: 100%;
		margin: 0 auto;
		box-sizing: border-box;
	}

	.boot {
		color: #64748b;
	}
</style>
