<!-- #region TopNavbar [C:4] [TYPE Component] [SEMANTICS navbar, search, activity, user-menu, environment] -->
<!-- @ingroup Layout -->
<!-- @BRIEF Unified top navigation bar with Logo, global search, environment selector, activity indicator, user menu, and assistant toggle. -->
<!-- @LAYER UI -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:activityStore] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:authStore] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:environmentContextStore] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:sidebarStore] -->
<!-- @RELATION BINDS_TO -> [EXT:frontend:taskDrawerStore] -->
<!-- @RELATION DISPATCHES -> [EXT:frontend:assistantChatStore] -->
<!-- @RELATION DEPENDS_ON -> [Icon] -->
<!-- @RELATION DEPENDS_ON -> [LanguageSwitcher] -->
<!-- @UX_STATE Idle -> Navbar showing current state with all controls. -->
<!-- @UX_STATE SearchFocused -> Search input expands with focus styling. -->
<!-- @UX_STATE Searching -> Search dropdown open while async results load. -->
<!-- @UX_FEEDBACK Activity badge shows count of running tasks. -->
<!-- @UX_FEEDBACK Environment selector shows PROD context in red. -->
<!-- @UX_RECOVERY Click outside closes dropdowns. -->
<script lang="ts">
  /**
   * @PURPOSE: Unified top navigation bar with Logo, Search, Activity, and User menu
   * @LAYER UI
   * @RELATION BINDS_TO -> activity
   * @RELATION BINDS_TO -> auth
   * @RELATION BINDS_TO -> environmentContext
   * @RELATION DISPATCHES -> [EXT:frontend:taskDrawer]
   * @RELATION DISPATCHES -> assistantChat
   * @RELATION DEPENDS_ON -> Icon
   * @RELATION DEPENDS_ON -> LanguageSwitcher
   * @SEMANTICS: Navigation, UserSession
   * @PRE: Auth, environment, and task stores are initialized before interactive navbar actions are used.
   * @POST: Search, environment selection, and global action controls remain synchronized with current app state.
   * @SIDE_EFFECT: Reads profile preferences, performs search API requests, and triggers drawer, auth, and assistant actions.
   * @DATA_CONTRACT: SearchQuery -> SearchSection[] | ProfilePreferences -> TaskDrawerPreference
   * @INVARIANT: Always visible on non-login pages
   * @UX_REACTIVITY: Uses $state for menu/search state and $derived projections from app stores.
   *
   * @UX_STATE: Idle -> Navbar showing current state
   * @UX_STATE: SearchFocused -> Search input expands
   * @UX_STATE: Searching -> Search dropdown stays open while async results are loading.
   * @UX_FEEDBACK: Activity badge shows count of running tasks
   * @UX_RECOVERY: Click outside closes dropdowns
   * @UX_TEST: SearchFocused -> {focus: search input, expected: focused style class applied}
   * @UX_TEST: ActivityClick -> {click: activity button, expected: task drawer opens}
   *
   * @TEST_CONTRACT Component_TopNavbar ->
   * {
   *   required_props: {},
   *   optional_props: {},
   *   invariants: [
   *       "Displays user menu and handles logical toggling",
   *       "Initiates global search successfully taking debounce into account",
   *       "Correctly handles activity notification badge visibility"
   *   ]
   * }
   * @TEST_FIXTURE logged_in -> {"user": {"username": "admin"}}
   * @TEST_EDGE network_down -> search fetch fails, handles error state
   * @TEST_INVARIANT ui_consistency -> verifies: [logged_in]
   */

  import { onMount } from "svelte";
  import { log } from "$lib/cot-logger";
  import { goto } from "$app/navigation";
  import { ROUTES } from "$lib/routes";
  import { api } from "$lib/api.js";
  import { getReports } from "$lib/api/reports.js";
  import { activityStore } from "$lib/stores/activity.svelte.js";
  import {
    openDrawerForTask,
    openDrawer,
    setTaskDrawerAutoOpenPreference,
  } from "$lib/stores/taskDrawer.svelte.js";
  import { sidebarStore, toggleMobileSidebar } from "$lib/stores/sidebar.svelte.js";
  import { t } from "$lib/i18n/index.svelte.js";
  import { auth } from "$lib/auth/store.svelte.js";
  import { hasPermission } from "$lib/auth/permissions.js";
  import { toggleAssistantChat } from "$lib/stores/assistantChat.svelte.js";
  import { Icon } from "$lib/ui";
  import LanguageSwitcher from "$lib/ui/LanguageSwitcher.svelte";
  import {
    environmentContextStore,
    initializeEnvironmentContext,
    setSelectedEnvironment,
    selectedEnvironmentStore,
  } from "$lib/stores/environmentContext.svelte.js";
  let showUserMenu = $state(false);
  let isSearchFocused = $state(false);
  let searchQuery = $state("");
  let showSearchDropdown = $state(false);
  let isSearchLoading = $state(false);
  let groupedSearchResults = $state([]);
  let searchTimer = null;

  const SEARCH_DEBOUNCE_MS = 250;
  const SEARCH_MIN_LENGTH = 2;
  const SEARCH_LIMIT = 5;

  let _authState = $state({ user: null, token: null, isAuthenticated: false, loading: true });
  onMount(() => {
    const unsub = auth.subscribe(v => { _authState = v; });
    return unsub;
  });

  let isExpanded = $derived(sidebarStore.value?.isExpanded ?? true);
  let activeCount = $derived(activityStore.value?.activeCount || 0);
  let recentTasks = $derived(activityStore.value?.recentTasks || []);
  let user = $derived(_authState.user || null);
  let canOpenSettings = $derived(
    hasPermission(user, "admin:settings", "READ"),
  );
  let globalEnvironments = $derived(
    $environmentContextStore?.environments || [],
  );
  let globalSelectedEnvId = $derived(
    $environmentContextStore?.selectedEnvId || "",
  );
  let globalSelectedEnv = $derived($selectedEnvironmentStore);
  let selectedEnvironmentValue = $state("");
  let isProdContext = $derived(
    String(globalSelectedEnv?.stage || "").toUpperCase() === "PROD" ||
      Boolean(globalSelectedEnv?.is_production),
  );

  $effect(() => {
    selectedEnvironmentValue = globalSelectedEnvId;
  });

  function toggleUserMenu(event) {
    event.stopPropagation();
    showUserMenu = !showUserMenu;
  }

  function closeUserMenu() {
    showUserMenu = false;
  }

  function handleLogout() {
    auth.logout();
    closeUserMenu();
    window.location.href = ROUTES.login();
  }

  function handleActivityClick() {
    const runningTask = recentTasks.find((t) => t.status === "RUNNING");
    if (runningTask) {
      openDrawerForTask(runningTask.taskId);
    } else if (recentTasks.length > 0) {
      openDrawerForTask(recentTasks[recentTasks.length - 1].taskId);
    } else {
      openDrawer();
    }
  }

  function handleAssistantClick() {
    goto("/agent");
  }

  async function hydrateTaskDrawerPreference() {
    try {
      const response = await api.getProfilePreferences();
      const autoOpenTaskDrawer = response?.preference?.auto_open_task_drawer;
      setTaskDrawerAutoOpenPreference(autoOpenTaskDrawer !== false);
    } catch (error) {
      log("TopNavbar", "EXPLORE", "Failed to hydrate task drawer preference", {}, error instanceof Error ? error.message : String(error));
    }
  }

  function handleSearchFocus() {
    isSearchFocused = true;
    showSearchDropdown = groupedSearchResults.length > 0;
  }

  function clearSearchState() {
    showSearchDropdown = false;
    isSearchLoading = false;
    groupedSearchResults = [];
  }

  function handleDocumentClick(event) {
    if (!event.target.closest(".user-menu-container")) {
      closeUserMenu();
    }
    if (!event.target.closest(".global-search-container")) {
      isSearchFocused = false;
      clearSearchState();
    }
  }

  function handleHamburgerClick(event) {
    event.stopPropagation();
    toggleMobileSidebar();
  }

  function handleGlobalEnvironmentChange(event) {
    const nextEnvId = event.currentTarget.value;
    selectedEnvironmentValue = nextEnvId;
    setSelectedEnvironment(nextEnvId);
    if (searchQuery.trim().length >= SEARCH_MIN_LENGTH) {
      triggerSearch(searchQuery.trim());
    }
  }

  function buildSearchResultSections(
    dashboardResponse,
    datasetResponse,
    tasksResponse,
    reportsResponse,
    query,
  ) {
    const dashboards = (dashboardResponse?.dashboards || []).slice(
      0,
      SEARCH_LIMIT,
    );
    const datasets = (datasetResponse?.datasets || []).slice(0, SEARCH_LIMIT);
    const dashboardItems = dashboards.map((dashboard) => ({
      key: `dashboard-${dashboard.id}`,
      type: "dashboard",
      title: dashboard.title || dashboard.dashboard_title || `#${dashboard.id}`,
      subtitle: `ID: ${dashboard.id}`,
      href: `/dashboards/${encodeURIComponent(String(dashboard.slug || dashboard.id))}?env_id=${encodeURIComponent(globalSelectedEnvId)}`,
    }));
    const datasetItems = datasets.map((dataset) => ({
      key: `dataset-${dataset.id}`,
      type: "dataset",
      title: dataset.table_name || `#${dataset.id}`,
      subtitle: dataset.schema || "-",
      href: `/datasets/${dataset.id}?env_id=${encodeURIComponent(globalSelectedEnvId)}`,
    }));
    const q = String(query || "").toLowerCase();
    const tasks = (tasksResponse || []).slice(0, 30);
    const taskItems = tasks
      .filter((task) => {
        const haystack =
          `${task?.id || ""} ${task?.plugin_id || ""} ${task?.status || ""}`.toLowerCase();
        return q && haystack.includes(q);
      })
      .slice(0, SEARCH_LIMIT)
      .map((task) => ({
        key: `task-${task.id}`,
        type: "task",
        title: task.plugin_id || "task",
        subtitle: `${task.id} · ${task.status || "-"}`,
        taskId: task.id,
      }));
    const reportItems = (reportsResponse?.items || [])
      .slice(0, SEARCH_LIMIT)
      .map((report) => ({
        key: `report-${report.report_id}`,
        type: "report",
        title: report.summary || report.report_id,
        subtitle: `${report.task_type || "-"} · ${report.status || "-"}`,
        href: "/reports",
      }));

    const sections = [];
    if (dashboardItems.length > 0) {
      sections.push({
        key: "dashboards",
        label: $t.nav?.dashboards || "Dashboards",
        items: dashboardItems,
      });
    }
    if (datasetItems.length > 0) {
      sections.push({
        key: "datasets",
        label: $t.nav?.datasets || "Datasets",
        items: datasetItems,
      });
    }
    if (taskItems.length > 0) {
      sections.push({
        key: "tasks",
        label: $t.nav?.tasks || "Tasks",
        items: taskItems,
      });
    }
    if (reportItems.length > 0) {
      sections.push({
        key: "reports",
        label: $t.nav?.reports || "Reports",
        items: reportItems,
      });
    }
    return sections;
  }

  async function triggerSearch(query) {
    const normalizedQuery = String(query || "").trim();
    if (normalizedQuery.length < SEARCH_MIN_LENGTH || !globalSelectedEnvId) {
      clearSearchState();
      return;
    }
    isSearchLoading = true;
    showSearchDropdown = true;
    try {
      const [
        dashboardResponse,
        datasetResponse,
        tasksResponse,
        reportsResponse,
      ] = await Promise.all([
        api.getDashboards(globalSelectedEnvId, {
          search: normalizedQuery,
          page: 1,
          page_size: SEARCH_LIMIT,
        }),
        api.getDatasets(globalSelectedEnvId, {
          search: normalizedQuery,
          page: 1,
          page_size: SEARCH_LIMIT,
        }),
        api.getTasks({ limit: 30, offset: 0, search: normalizedQuery }),
        getReports({
          page: 1,
          page_size: SEARCH_LIMIT,
          search: normalizedQuery,
          sort_by: "updated_at",
          sort_order: "desc",
        }),
      ]);
      groupedSearchResults = buildSearchResultSections(
        dashboardResponse,
        datasetResponse,
        tasksResponse,
        reportsResponse,
        normalizedQuery,
      );
    } catch (error) {
      log("TopNavbar", "EXPLORE", "Global search failed", { query: normalizedQuery }, error instanceof Error ? error.message : String(error));
      groupedSearchResults = [];
    } finally {
      isSearchLoading = false;
    }
  }

  function handleSearchInput(event) {
    searchQuery = event.target.value;
    if (searchTimer) {
      clearTimeout(searchTimer);
    }
    searchTimer = setTimeout(() => {
      triggerSearch(searchQuery);
    }, SEARCH_DEBOUNCE_MS);
  }

  async function openSearchResult(item) {
    clearSearchState();
    searchQuery = "";
    isSearchFocused = false;
    if (item.type === "task" && item.taskId) {
      openDrawerForTask(item.taskId);
      return;
    }
    if (item.href) {
      await goto(item.href);
    }
  }

  async function handleSearchKeydown(event) {
    if (event.key === "Escape") {
      clearSearchState();
      searchQuery = "";
      isSearchFocused = false;
      return;
    }
    const firstItem = groupedSearchResults[0]?.items?.[0];
    if (event.key === "Enter" && firstItem) {
      event.preventDefault();
      await openSearchResult(firstItem);
    }
  }

  onMount(() => {
    void initializeEnvironmentContext();
    void hydrateTaskDrawerPreference();
    if (typeof document !== "undefined") {
      document.addEventListener("click", handleDocumentClick);
    }
    return () => {
      if (searchTimer) {
        clearTimeout(searchTimer);
      }
      if (typeof document !== "undefined") {
        document.removeEventListener("click", handleDocumentClick);
      }
    };
  });
</script>

<nav
  class="fixed left-0 right-0 top-0 z-40 flex h-16 max-w-full items-center justify-between overflow-hidden border-b border-border bg-surface-card px-3 shadow-sm sm:px-4
    {isExpanded ? 'md:left-[240px]' : 'md:left-16'}"
>
  <!-- Left section: Hamburger (mobile) + Logo -->
  <div class="flex min-w-0 items-center gap-2">
    <!-- Hamburger Menu (mobile only) -->
    <button
      class="rounded-lg p-2 text-text-muted transition-colors hover:bg-surface-muted md:hidden"
      onclick={handleHamburgerClick}
      aria-label={$t.common?.toggle_menu}
    >
      <Icon name="menu" size={22} />
    </button>

    <!-- Logo/Brand -->
    <a
      href="/"
      class="flex min-w-0 items-center text-xl font-bold text-text transition-colors hover:text-primary"
    >
      <span
        class="mr-2 inline-flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-sky-500 via-cyan-500 to-indigo-600 text-white shadow-sm"
      >
        <Icon name="layers" size={18} strokeWidth={2.1} />
      </span>
      <span class="hidden sm:inline">{$t.common?.brand}</span>
    </a>
  </div>

  <!-- Global search -->
  <div
    class="global-search-container relative flex-1 max-w-xl mx-4 hidden md:block"
  >
    <input
      id="global-search"
      name="global_search"
      aria-label={$t.common.search}
      type="text"
      class="w-full px-4 py-2 bg-surface-muted rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-ring transition-all
        {isSearchFocused ? 'bg-surface-card border border-primary-ring' : ''}"
      placeholder={$t.common.search}
      value={searchQuery}
      oninput={handleSearchInput}
      onfocus={handleSearchFocus}
      onkeydown={handleSearchKeydown}
    />
    {#if showSearchDropdown}
      <div
        class="absolute left-0 right-0 top-12 z-50 rounded-lg border border-border bg-surface-card shadow-lg"
      >
        {#if isSearchLoading}
          <div class="px-4 py-3 text-sm text-text-muted">
            {$t.common?.loading || "Loading..."}
          </div>
        {:else if groupedSearchResults.length === 0}
          <div class="px-4 py-3 text-sm text-text-muted">
            {$t.common?.not_found || "No results found"}
          </div>
        {:else}
          {#each groupedSearchResults as section}
            <div class="border-b border-border last:border-b-0">
              <div
                class="px-4 py-2 text-xs font-semibold uppercase tracking-wide text-text-muted"
              >
                {section.label}
              </div>
              {#each section.items as result}
                <button
                  class="flex w-full items-center justify-between px-4 py-2 text-left hover:bg-surface-page"
                  onclick={() => openSearchResult(result)}
                >
                  <div class="min-w-0">
                    <div class="truncate text-sm font-medium text-text">
                      {result.title}
                    </div>
                    <div class="truncate text-xs text-text-muted">
                      {result.subtitle}
                    </div>
                  </div>
                </button>
              {/each}
            </div>
          {/each}
        {/if}
      </div>
    {/if}
  </div>

  <!-- Nav Actions -->
  <div class="flex shrink-0 items-center gap-1 sm:gap-2 md:gap-4">
    {#if globalEnvironments.length > 0}
      <div class="hidden lg:flex items-center gap-2">
        <select
          id="global-environment-select"
          name="global_environment"
          class="h-9 rounded-lg border px-3 text-sm font-medium focus:outline-none focus:ring-2
            {isProdContext
            ? 'border-destructive-ring bg-destructive-light text-destructive focus:ring-destructive-ring'
            : 'border-border-strong bg-surface-card text-text focus:ring-sky-200'}"
          bind:value={selectedEnvironmentValue}
          onchange={handleGlobalEnvironmentChange}
          aria-label={$t.dashboard?.environment || "Environment"}
          title={$t.dashboard?.environment || "Environment"}
        >
          {#each globalEnvironments as env}
            <option value={env.id}>
              {env.name}{(String(env.stage || "").toUpperCase() === "PROD" || env.is_production) ? " [PROD]" : ""}
            </option>
          {/each}
        </select>

      </div>
    {/if}

    <LanguageSwitcher />

    <!-- Assistant -->
    <button
      class="rounded-lg p-2 text-text-muted transition-colors hover:bg-surface-muted"
      onclick={handleAssistantClick}
      aria-label={$t.assistant?.open}
      title={$t.assistant?.title}
    >
      <Icon name="clipboard" size={22} />
    </button>

    <!-- Activity Indicator -->
    <div
      class="relative cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-surface-muted"
      onclick={handleActivityClick}
      onkeydown={(e) =>
        (e.key === "Enter" || e.key === " ") && handleActivityClick()}
      role="button"
      tabindex="0"
      aria-label={$t.common?.activity}
    >
      <Icon name="activity" size={22} />
      {#if activeCount > 0}
        <span
          class="absolute -top-1 -right-1 bg-destructive text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center"
          >{activeCount}</span
        >
      {/if}
    </div>

    <!-- User Menu -->
    <div class="user-menu-container relative">
      <div
        class="w-8 h-8 rounded-full bg-primary text-white flex items-center justify-center cursor-pointer hover:bg-primary-hover transition-colors"
        onclick={toggleUserMenu}
        onkeydown={(e) =>
          (e.key === "Enter" || e.key === " ") && toggleUserMenu(e)}
        role="button"
        tabindex="0"
        aria-label={$t.common?.user_menu}
      >
        {#if user}
          <span
            >{user.username ? user.username.charAt(0).toUpperCase() : "U"}</span
          >
        {:else}
          <span>U</span>
        {/if}
      </div>

      <!-- User Dropdown -->
      <div
        class="absolute right-0 mt-2 w-48 bg-surface-card rounded-lg shadow-lg border border-border py-1 z-50 {showUserMenu
          ? ''
          : 'hidden'}"
      >
        <div class="px-4 py-2 text-sm text-text">
          <strong>{user?.username || $t.common?.user}</strong>
        </div>
        <div class="border-t border-border my-1"></div>
        {#if canOpenSettings}
          <div
            class="px-4 py-2 text-sm text-text hover:bg-surface-muted cursor-pointer"
            onclick={() => {
              window.location.href = ROUTES.settings.general();
            }}
            onkeydown={(e) =>
              (e.key === "Enter" || e.key === " ") &&
              (window.location.href = ROUTES.settings.general())}
            role="button"
            tabindex="0"
          >
            {$t.nav?.settings}
          </div>
        {/if}
        <div
          class="px-4 py-2 text-sm text-destructive hover:bg-destructive-light cursor-pointer"
          onclick={handleLogout}
          onkeydown={(e) =>
            (e.key === "Enter" || e.key === " ") && handleLogout()}
          role="button"
          tabindex="0"
        >
          {$t.common?.logout}
        </div>
      </div>
    </div>
  </div>
</nav>

<!-- #endregion TopNavbar -->
