<script lang="ts">
	import type { Snippet } from 'svelte';
	import { get } from 'svelte/store';
	import { page } from '$app/stores';
	import { isAuthenticated, authLoading, currentUser } from '$lib/stores/auth';
	import { login, isLoggingOut } from '$lib/auth';
	import { getSupabase } from '$lib/supabase';
	import { setThemePreference, themePreference, type ThemePreference } from '$lib/theme';
	import { currentCollective } from '$lib/stores/collective';
	import { setSyncContext } from '$lib/sync';
	import DesktopSidebar from '$lib/components/layout/DesktopSidebar.svelte';
	import MobileTopBar from '$lib/components/layout/MobileTopBar.svelte';
	import BottomTabBar from '$lib/components/layout/BottomTabBar.svelte';
	import MobileDrawer from '$lib/components/layout/MobileDrawer.svelte';
	import UndoToast from '$lib/components/UndoToast.svelte';
	import * as m from '$lib/paraglide/messages';

	let { children }: { children: Snippet } = $props();

	let drawerOpen = $state(false);

	// Detail routes render their own contextual header (back + title). On mobile
	// we hide the global MobileTopBar for those paths so the contextual header
	// becomes the only top chrome. BottomTabBar stays visible (user can still
	// jump between sections). Modo Compra covers everything with its own z-50
	// overlay, so it effectively hides bottom too — handled locally.
	const detailRoutePattern = /^\/(lists\/[0-9a-f-]+(?:\/session)?|tasks\/[0-9a-f-]+|notes\/[0-9a-f-]+)\/?$/;
	const isDetailRoute = $derived(detailRoutePattern.test($page.url.pathname));

	// Fase 9.2: cap the reading column at max-w-2xl on >=md viewports for
	// content detail routes (lists/[id], tasks/[id], notes/[id]). Mobile
	// stays full-width. Shopping session is explicitly excluded — the mode
	// is immersive and fills the viewport.
	const readingRoutePattern = /^\/(lists\/[0-9a-f-]+|tasks\/[0-9a-f-]+|notes\/[0-9a-f-]+)\/?$/;
	const isReadingRoute = $derived(readingRoutePattern.test($page.url.pathname));

	// When auth resolves as unauthenticated, redirect to Keycloak.
	// This runs after onAuthStateChange fires (authLoading = false), so there
	// is no race with Supabase's async localStorage initialisation.
	// `isLoggingOut` suppresses re-login while logout() is mid-flight between
	// signOut() and the Keycloak end-session redirect — otherwise we'd race
	// the navigation and clobber the PKCE verifier.
	$effect(() => {
		if (!$authLoading && !$isAuthenticated && !isLoggingOut) {
			login();
		}
	});

	// Fase 9.3: keep the offline-sync queue's context (user_id +
	// collective_id) in sync with the active session. Without this the
	// queue's conflict-detection short-circuits silently (it needs a
	// known user_id to satisfy sync_conflicts.user_id RLS).
	$effect(() => {
		setSyncContext({
			currentUserId: $currentUser?.id ?? null,
			collectiveId: $currentCollective?.id ?? null
		});
	});

	// Fase 9.1: cross-device theme sync. When the user logs in, read
	// public.users.theme and adopt it if it differs from the local
	// preference. The anti-FOUC inline script in app.html and initTheme()
	// have already set the local default from localStorage, so this only
	// overrides when the server says otherwise. Defer the PostgREST query
	// out of the auth callback (gotcha already documented for collectives).
	let lastSyncedUserId: string | null = $state(null);
	$effect(() => {
		const userId = $currentUser?.id ?? null;
		if (!userId || userId === lastSyncedUserId) return;
		lastSyncedUserId = userId;
		setTimeout(async () => {
			const { data } = await getSupabase()
				.from('users')
				.select('theme')
				.eq('id', userId)
				.single();
			const remote = (data?.theme ?? null) as ThemePreference | null;
			if (
				remote &&
				(remote === 'light' || remote === 'dark' || remote === 'system') &&
				remote !== get(themePreference)
			) {
				setThemePreference(remote);
			}
		}, 0);
	});
</script>

{#if $authLoading}
	<div class="flex h-screen items-center justify-center bg-background">
		<span class="text-sm text-text-secondary">{m.loading()}</span>
	</div>
{:else if $isAuthenticated}
	<!-- Mobile: column stack (top bar → main → bottom tab bar).
	     Desktop: row (sidebar | main). -->
	<div class="flex h-screen flex-col overflow-hidden bg-background md:flex-row">
		<DesktopSidebar />

		{#if !isDetailRoute}
			<MobileTopBar onMenu={() => (drawerOpen = true)} />
		{/if}
		<MobileDrawer bind:open={drawerOpen} />

		<main class="flex flex-1 flex-col overflow-hidden bg-background pb-16 md:pb-0">
			{#if isReadingRoute}
				<!-- Fase 9.2: cap reading column at 42rem on >=md, full-width on mobile. -->
				<div
					data-testid="reading-column"
					class="flex flex-1 flex-col overflow-hidden md:mx-auto md:w-full md:max-w-2xl"
				>
					{@render children()}
				</div>
			{:else}
				{@render children()}
			{/if}
		</main>

		<BottomTabBar />
		<UndoToast />
	</div>
{/if}
