<script lang="ts">
  import { onMount, onDestroy, setContext } from "svelte";
  import { goto } from "$app/navigation";
  import { page } from "$app/stores";
  import { isAuthenticated, isLoading as isAuthLoading } from "$lib/stores/auth";
  import { library } from "$lib/stores/library";
  import { useScrollGuard } from "$lib/composables/useScrollGuard";
  import Search from "$lib/components/Search.svelte";
  import {
    resolveSearchScope,
    searchRouteUrl,
    shouldNavigateToSearch,
    type SearchScope,
  } from "$lib/utils/searchScope";
  import AppHeader from "$lib/components/AppHeader.svelte";
  import BottomUi from "$lib/components/BottomUi.svelte";
  import SleepTimerModal from "$lib/components/player/SleepTimerModal.svelte";

  // Scroll guard prevents accidental taps on library cards during/after scrolling (Android)
  const scrollGuard = useScrollGuard(300);
  setContext("scrollGuard", scrollGuard);

  let { children } = $props();

  let searchQuery = $state("");
  let showSleepTimerModal = $state(false);

  onMount(() => {
    return () => {
      scrollGuard.cleanup();
    };
  });

  // Redirect to login if not authenticated
  $effect(() => {
    if (!$isAuthLoading && !$isAuthenticated) {
      goto("/");
    }
  });

  // The header search outlives navigation, so the route seeds the scope only
  // while no search is active. Once the user has typed (or picked a chip),
  // their scope governs until they clear the query — navigating must not snap
  // a widened search back to the section they happen to be in.
  // TRACES: UR-049 | DR-064
  let searchScope = $state<SearchScope>(resolveSearchScope($page.url.pathname));

  $effect(() => {
    const pathname = $page.url.pathname;
    if (!searchQuery.trim()) {
      searchScope = resolveSearchScope(pathname);
    }
  });

  // The header bar is a *navigator*, not a second results surface: /search is
  // the only route that renders searchResults, so searching here routes there
  // with the query + route-derived scope in the URL. Previously this ran
  // library.search() in place, which was invisible on every /library/** page
  // except /library itself.
  // TRACES: UR-049 | DR-063
  async function handleSearch(query: string) {
    if (!query.trim()) {
      library.clearSearch();
      return;
    }
    if (shouldNavigateToSearch($page.url.pathname, query)) {
      await goto(searchRouteUrl(query, searchScope));
      // The query now lives in the URL; clear the header input so returning to
      // a library page does not leave a stale term sitting in the box.
      searchQuery = "";
    }
  }
</script>

{#if $isAuthLoading}
  <div class="min-h-screen flex items-center justify-center">
    <div class="w-8 h-8 border-2 border-[var(--color-jellyfin)] border-t-transparent rounded-full animate-spin"></div>
  </div>
{:else if $isAuthenticated}
  <div class="h-screen flex flex-col overflow-hidden">
    <!-- Header (shared across all authenticated chrome; library supplies search) -->
    <AppHeader search={librarySearch} />

    {#snippet librarySearch()}
      <!-- Scope chips live on /search, which owns the results. -->
      <Search
        bind:value={searchQuery}
        placeholder="Search your library..."
        onSearch={handleSearch}
      />
    {/snippet}

    <!-- Main content. The BottomUi below is an in-flow flex sibling, so this
         scroller is physically bounded above it and its last row can never
         render behind the nav — no measurement, no reserved padding. -->
    <main
      class="flex-1 overflow-y-auto p-4 min-h-0"
      style="overscroll-behavior: contain"
      onscroll={scrollGuard.onScroll}
    >
      {@render children()}
    </main>

    <!-- Bottom UI (mini player + nav), in-flow below the scroller. -->
    <BottomUi />

    <!-- Sleep Timer Modal -->
    <SleepTimerModal
      isOpen={showSleepTimerModal}
      onClose={() => showSleepTimerModal = false}
    />
  </div>
{/if}
