<script lang="ts">
    import { onMount, onDestroy } from 'svelte';
    import { goto } from '$app/navigation';
    import { logout } from '$lib/api/auth';
    import { preferences } from '$lib/preferences.svelte';
    import { session } from '$lib/session.svelte';
    import { theme } from '$lib/theme.svelte';
    import Upload from '@lucide/svelte/icons/upload';
    import UserCircle from '@lucide/svelte/icons/user-circle';
    import Bookmark from '@lucide/svelte/icons/bookmark';
    import FolderOpen from '@lucide/svelte/icons/folder-open';
    import LogOut from '@lucide/svelte/icons/log-out';
    import '$lib/styles/tokens.css';

    let { children } = $props();
    let loggingOut = $state(false);
    let headerEl: HTMLElement | undefined = $state();

    onMount(() => {
        theme.init();
        preferences.init();
        if (!session.loaded) session.refresh();

        // Publish the header's measured height as a CSS custom
        // property so sticky descendants (e.g. the reader nav) can
        // pin themselves directly below it without guessing. A
        // ResizeObserver keeps it in sync as the viewport reflows
        // (the nav `flex-wrap: wrap`s on narrow widths), the user
        // zooms, or fonts swap. Hard-coded pixel offsets in tokens
        // are wrong in principle — actual height varies with all
        // of the above.
        if (!headerEl) return;
        const publish = () => {
            document.documentElement.style.setProperty(
                '--app-header-h',
                `${headerEl!.offsetHeight}px`
            );
        };
        publish();
        const ro = new ResizeObserver(publish);
        ro.observe(headerEl);
        return () => ro.disconnect();
    });

    // Pull fresh server preferences whenever the user changes (login,
    // logout, account switch). The store's seq guard keeps the most recent
    // response authoritative.
    $effect(() => {
        if (session.user) preferences.refresh();
    });

    onDestroy(() => theme.destroy());

    async function handleLogout() {
        loggingOut = true;
        try {
            await logout();
        } finally {
            session.setUser(null);
            // Don't let user A's reader preferences linger for the next
            // person who uses this browser (or as guest state for the
            // same user). Resets state + localStorage.
            preferences.clearForLogout();
            loggingOut = false;
            goto('/login');
        }
    }
</script>

<header bind:this={headerEl}>
    <nav aria-label="primary">
        <a class="brand" href="/">Mangalord</a>
        <a class="nav-link" href="/upload">
            <Upload size={18} aria-hidden="true" />
            <span>Upload</span>
        </a>
        <a class="nav-link" href="/profile" data-testid="nav-profile">
            <UserCircle size={18} aria-hidden="true" />
            <span>Profile</span>
        </a>
        <a class="nav-link" href="/bookmarks">
            <Bookmark size={18} aria-hidden="true" />
            <span>Bookmarks</span>
        </a>
        <a class="nav-link" href="/collections">
            <FolderOpen size={18} aria-hidden="true" />
            <span>Collections</span>
        </a>
    </nav>
    <div class="session" data-testid="session-area">
        {#if !session.loaded}
            <span data-testid="session-loading" aria-busy="true">…</span>
        {:else if session.user}
            <span class="username" data-testid="session-user">{session.user.username}</span>
            <button
                class="icon-btn"
                type="button"
                onclick={handleLogout}
                disabled={loggingOut}
                aria-label="Logout"
                title="Logout"
            >
                {#if loggingOut}
                    <span class="logging-out">…</span>
                {:else}
                    <LogOut size={18} aria-hidden="true" />
                {/if}
            </button>
        {:else}
            <a class="text-link" href="/login" data-testid="nav-login">Login</a>
            <a class="text-link" href="/register" data-testid="nav-register">Register</a>
        {/if}
    </div>
</header>

<main>
    {@render children()}
</main>

<style>
    header {
        padding: var(--space-3) var(--space-4);
        border-bottom: 1px solid var(--border);
        background: var(--surface);
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: var(--space-4);
        flex-wrap: wrap;
    }

    nav {
        display: flex;
        align-items: center;
        gap: var(--space-1);
    }

    .brand {
        font-weight: var(--weight-semibold);
        font-size: var(--font-lg);
        color: var(--text);
        padding: var(--space-2) var(--space-3);
        margin-right: var(--space-2);
    }

    .brand:hover {
        text-decoration: none;
        color: var(--primary);
    }

    .nav-link {
        display: inline-flex;
        align-items: center;
        gap: var(--space-2);
        color: var(--text);
        padding: var(--space-2) var(--space-3);
        border-radius: var(--radius-md);
        font-size: var(--font-sm);
        transition: background var(--transition);
    }

    .nav-link:hover {
        background: var(--surface-elevated);
        text-decoration: none;
    }

    .text-link {
        color: var(--primary);
        padding: var(--space-2) var(--space-3);
        font-size: var(--font-sm);
    }

    /* App frame: header is fixed at the viewport top with a slide
       transition so reader fullscreen (set via `data-reader-fullscreen`
       on `<html>`) can hide it without jolting the layout. `main` pays
       the gap with a matching padding-top that animates in lockstep. */
    header {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: var(--z-sticky);
        transform: translateY(0);
        transition: transform 220ms ease-out;
    }

    :global(html[data-reader-fullscreen='true']) header {
        transform: translateY(-100%);
    }

    .session {
        display: flex;
        align-items: center;
        gap: var(--space-1);
    }

    .username {
        color: var(--text-muted);
        font-size: var(--font-sm);
        padding: 0 var(--space-2);
    }

    .icon-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 36px;
        height: 36px;
        padding: 0;
        background: transparent;
        color: var(--text-muted);
        border: 1px solid transparent;
        border-radius: var(--radius-md);
    }

    .icon-btn:hover:not(:disabled) {
        background: var(--surface-elevated);
        color: var(--text);
    }

    .logging-out {
        font-size: var(--font-base);
        line-height: 1;
    }

    main {
        padding: var(--space-4);
        /* Reserve room for the fixed header so its presence doesn't
           overlap content. The header height comes from a runtime
           ResizeObserver (see onMount above) so this always tracks
           the rendered size. */
        padding-top: calc(var(--app-header-h) + var(--space-4));
        max-width: 64rem;
        margin: 0 auto;
        transition: padding-top 220ms ease-out;
    }

    :global(html[data-reader-fullscreen='true']) main {
        /* No top reservation in focus mode — the chapter image runs
           edge-to-edge once the header has slid off. */
        padding-top: 0;
    }
</style>
