import { Outlet, useNavigate, useLocation } from 'react-router';
import { config } from '../config';
import {
  AppShell,
  Sidebar,
  SidebarTree,
  StatusDot,
  TopBar,
  CommandPalette,
  CommandPaletteProvider,
  GlobalFilterProvider,
  ToastProvider,
  BreadcrumbProvider,
  useCommandPalette,
  useGlobalFilters,
  useStarred,
} from '@cameleer/design-system';
import type { SearchResult, SidebarTreeNode } from '@cameleer/design-system';
import sidebarLogo from '@cameleer/design-system/assets/cameleer3-48.png';
import { Box, Settings, FileText, ChevronRight, Square, Pause, Star, X } from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouteCatalog } from '../api/queries/catalog';
import { useAgents } from '../api/queries/agents';
import { useSearchExecutions, useAttributeKeys } from '../api/queries/executions';
import { useUsers, useGroups, useRoles } from '../api/queries/admin/rbac';
import type { UserDetail, GroupDetail, RoleDetail } from '../api/queries/admin/rbac';
import { useAuthStore, useIsAdmin } from '../auth/auth-store';
import { useEnvironmentStore } from '../api/environment-store';
import { useState, useMemo, useCallback, useEffect, useRef, createElement } from 'react';
import type { ReactNode } from 'react';
import { ContentTabs } from './ContentTabs';
import { EnvironmentSelector } from './EnvironmentSelector';
import { useScope } from '../hooks/useScope';
import {
  buildAppTreeNodes,
  buildAdminTreeNodes,
  formatCount,
  readCollapsed,
  writeCollapsed,
} from './sidebar-utils';
import type { SidebarApp } from './sidebar-utils';

/* ------------------------------------------------------------------ */
/*  Search data builder                                               */
/* ------------------------------------------------------------------ */

function buildSearchData(
  catalog: any[] | undefined,
  agents: any[] | undefined,
  attrKeys: string[] | undefined,
): SearchResult[] {
  if (!catalog) return [];
  const results: SearchResult[] = [];

  for (const app of catalog) {
    const liveAgents = (app.agents || []).filter((a: any) => a.status === 'live').length;
    results.push({
      id: app.appId,
      category: 'application',
      title: app.appId,
      badges: [{ label: (app.health || 'unknown').toUpperCase(), color: healthToSearchColor(app.health) }],
      meta: `${(app.routes || []).length} routes · ${(app.agents || []).length} agents (${liveAgents} live) · ${(app.exchangeCount ?? 0).toLocaleString()} exchanges`,
      path: `/exchanges/${app.appId}`,
    });

    for (const route of (app.routes || [])) {
      results.push({
        id: `${app.appId}/${route.routeId}`,
        category: 'route',
        title: route.routeId,
        badges: [{ label: app.appId }],
        meta: `${(route.exchangeCount ?? 0).toLocaleString()} exchanges`,
        path: `/exchanges/${app.appId}/${route.routeId}`,
      });
    }
  }

  if (agents) {
    for (const agent of agents) {
      results.push({
        id: agent.instanceId,
        category: 'agent',
        title: agent.displayName,
        badges: [{ label: (agent.status || 'unknown').toUpperCase(), color: healthToSearchColor((agent.status || '').toLowerCase()) }],
        meta: `${agent.applicationId} · ${agent.version || ''}${agent.tps != null ? ` · ${agent.tps.toFixed(1)} msg/s` : ''}`,
        path: `/runtime/${agent.applicationId}/${agent.instanceId}`,
      });
    }
  }

  if (attrKeys) {
    for (const key of attrKeys) {
      results.push({
        id: `attr-key-${key}`,
        category: 'attribute',
        title: key,
        meta: 'attribute key',
      });
    }
  }

  return results;
}

function buildAdminSearchData(
  users: UserDetail[] | undefined,
  groups: GroupDetail[] | undefined,
  roles: RoleDetail[] | undefined,
): SearchResult[] {
  const results: SearchResult[] = [];

  if (users) {
    for (const u of users) {
      results.push({
        id: `user:${u.userId}`,
        category: 'user',
        title: u.displayName || u.userId,
        meta: u.userId,
        path: '/admin/rbac',
      });
    }
  }

  if (groups) {
    for (const g of groups) {
      results.push({
        id: `group:${g.id}`,
        category: 'group',
        title: g.name,
        meta: g.parentGroupId ? `parent: ${g.parentGroupId}` : 'top-level group',
        path: '/admin/rbac',
      });
    }
  }

  if (roles) {
    for (const r of roles) {
      results.push({
        id: `role:${r.id}`,
        category: 'role',
        title: r.name,
        meta: r.scope,
        path: '/admin/rbac',
      });
    }
  }

  return results;
}

function healthToSearchColor(health: string): string {
  switch (health) {
    case 'live': return 'success';
    case 'stale': return 'warning';
    case 'dead': return 'error';
    default: return 'auto';
  }
}

function formatDuration(ms: number): string {
  if (ms >= 60_000) return `${(ms / 1000).toFixed(0)}s`;
  if (ms >= 1000) return `${(ms / 1000).toFixed(2)}s`;
  return `${ms}ms`;
}

function statusToColor(status: string): string {
  switch (status) {
    case 'COMPLETED': return 'success';
    case 'FAILED': return 'error';
    case 'RUNNING': return 'running';
    default: return 'warning';
  }
}

function useDebouncedValue<T>(value: T, delayMs: number): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delayMs);
    return () => clearTimeout(timer);
  }, [value, delayMs]);
  return debounced;
}

/* ------------------------------------------------------------------ */
/*  Icon factories                                                    */
/* ------------------------------------------------------------------ */

function makeStatusDot(health: string) {
  return createElement(StatusDot, { variant: health as any });
}

function makeChevron() {
  return createElement(ChevronRight, { size: 14 });
}

function makeStopIcon() {
  return createElement(Square, { size: 12, style: { color: 'var(--error)' } });
}

function makePauseIcon() {
  return createElement(Pause, { size: 12, style: { color: 'var(--amber)' } });
}

/* ------------------------------------------------------------------ */
/*  Starred items                                                     */
/* ------------------------------------------------------------------ */

interface StarredItem {
  starKey: string;
  label: string;
  icon?: ReactNode;
  path: string;
  parentApp?: string;
}

function collectStarredItems(apps: SidebarApp[], starredIds: Set<string>): StarredItem[] {
  const items: StarredItem[] = [];
  for (const app of apps) {
    if (starredIds.has(`app:${app.id}`)) {
      items.push({ starKey: `app:${app.id}`, label: app.name, icon: makeStatusDot(app.health), path: `/apps/${app.id}` });
    }
    for (const route of app.routes) {
      const key = `route:${app.id}/${route.id}`;
      if (starredIds.has(key)) {
        items.push({ starKey: key, label: route.name, path: `/apps/${app.id}/${route.id}`, parentApp: app.name });
      }
    }
  }
  return items;
}

function StarredList({ items, onNavigate, onRemove }: { items: StarredItem[]; onNavigate: (path: string) => void; onRemove: (key: string) => void }) {
  if (items.length === 0) return null;
  return (
    <div style={{ padding: '4px 0' }}>
      {items.map((item) => (
        <div
          key={item.starKey}
          style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 12px', cursor: 'pointer', fontSize: 12, color: 'var(--sidebar-text)', borderRadius: 4 }}
          onClick={() => onNavigate(item.path)}
          role="button"
          tabIndex={0}
          onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onNavigate(item.path); }}
        >
          {item.icon && <span style={{ display: 'flex', alignItems: 'center', color: 'var(--sidebar-muted)' }}>{item.icon}</span>}
          <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {item.label}
            {item.parentApp && <span style={{ color: 'var(--sidebar-muted)', marginLeft: 4, fontSize: 10 }}>{item.parentApp}</span>}
          </span>
          <button
            style={{ background: 'none', border: 'none', padding: 2, cursor: 'pointer', color: 'var(--sidebar-muted)', display: 'flex', alignItems: 'center', opacity: 0.6 }}
            onClick={(e) => { e.stopPropagation(); onRemove(item.starKey); }}
            aria-label={`Remove ${item.label} from starred`}
          >
            <X size={10} />
          </button>
        </div>
      ))}
    </div>
  );
}

/* ------------------------------------------------------------------ */
/*  Section state keys                                                */
/* ------------------------------------------------------------------ */

const SK_APPS = 'sidebar:section:apps';
const SK_ADMIN = 'sidebar:section:admin';
const SK_COLLAPSED = 'sidebar:collapsed';

/* ------------------------------------------------------------------ */
/*  Main layout content                                               */
/* ------------------------------------------------------------------ */

function LayoutContent() {
  const navigate = useNavigate();
  const location = useLocation();
  const queryClient = useQueryClient();
  const { timeRange, autoRefresh, refreshTimeRange } = useGlobalFilters();

  // --- Role checks ----------------------------------------------------
  const isAdmin = useIsAdmin();

  // --- Environment filtering -----------------------------------------
  const selectedEnv = useEnvironmentStore((s) => s.environment);
  const setSelectedEnvRaw = useEnvironmentStore((s) => s.setEnvironment);

  const { data: catalog } = useRouteCatalog(timeRange.start.toISOString(), timeRange.end.toISOString(), selectedEnv);
  const { data: allAgents } = useAgents();  // unfiltered — for environment discovery
  const { data: agents } = useAgents(undefined, undefined, selectedEnv);  // filtered — for sidebar/search
  const { data: attributeKeys } = useAttributeKeys();

  // Extract distinct environments from ALL agents (not filtered by selected env)
  const environments: string[] = useMemo(() => {
    if (!allAgents) return ['default'];
    const envSet = new Set<string>();
    for (const a of allAgents as any[]) {
      envSet.add(a.environmentId || 'default');
    }
    return [...envSet].sort();
  }, [allAgents]);

  // --- Admin search data (only fetched on admin pages) ----------------
  const isAdminPage = location.pathname.startsWith('/admin');
  const { data: adminUsers } = useUsers(isAdminPage);
  const { data: adminGroups } = useGroups(isAdminPage);
  const { data: adminRoles } = useRoles(isAdminPage);

  const { username, logout } = useAuthStore();
  const { open: paletteOpen, setOpen: setPaletteOpen } = useCommandPalette();
  const { scope, setTab } = useScope();

  // --- Starred items ------------------------------------------------
  const { starredIds, isStarred, toggleStar } = useStarred();

  // --- Sidebar collapse ---------------------------------------------
  const [sidebarCollapsed, setSidebarCollapsed] = useState(() => readCollapsed(SK_COLLAPSED, false));
  const handleCollapseToggle = useCallback(() => {
    setSidebarCollapsed((prev) => {
      writeCollapsed(SK_COLLAPSED, !prev);
      return !prev;
    });
  }, []);

  // --- Sidebar filter -----------------------------------------------
  const [filterQuery, setFilterQuery] = useState('');

  const setSelectedEnv = useCallback((env: string | undefined) => {
    setSelectedEnvRaw(env);
    setFilterQuery('');
    if (location.search) {
      navigate(location.pathname, { replace: true });
    }
    queryClient.invalidateQueries();
  }, [setSelectedEnvRaw, navigate, location.pathname, location.search, queryClient]);

  // --- Section open states ------------------------------------------
  const [appsOpen, setAppsOpen] = useState(() => isAdminPage ? false : readCollapsed(SK_APPS, true));
  const [adminOpen, setAdminOpen] = useState(() => isAdminPage ? true : readCollapsed(SK_ADMIN, false));
  const [starredOpen, setStarredOpen] = useState(true);

  // Accordion: entering admin collapses apps + starred; leaving restores
  const opsStateRef = useRef({ apps: appsOpen, starred: starredOpen });
  const prevAdminRef = useRef(isAdminPage);
  useEffect(() => {
    if (isAdminPage && !prevAdminRef.current) {
      opsStateRef.current = { apps: appsOpen, starred: starredOpen };
      setAppsOpen(false);
      setStarredOpen(false);
      setAdminOpen(true);
    } else if (!isAdminPage && prevAdminRef.current) {
      setAppsOpen(opsStateRef.current.apps);
      setStarredOpen(opsStateRef.current.starred);
      setAdminOpen(false);
    }
    prevAdminRef.current = isAdminPage;
  }, [isAdminPage]); // eslint-disable-line react-hooks/exhaustive-deps

  const toggleApps = useCallback(() => {
    if (isAdminPage) {
      navigate('/exchanges');
      return;
    }
    if (appsOpen) {
      // Already open — navigate to all applications
      if (!autoRefresh) {
        refreshTimeRange();
        queryClient.invalidateQueries();
      }
      navigate(`/${scope.tab}`);
    } else {
      setAppsOpen(true);
      writeCollapsed(SK_APPS, true);
    }
  }, [isAdminPage, appsOpen, navigate, scope.tab, autoRefresh, refreshTimeRange, queryClient]);

  const toggleAdmin = useCallback(() => {
    if (!isAdminPage) {
      navigate('/admin/rbac');
      return;
    }
    setAdminOpen((prev) => {
      writeCollapsed(SK_ADMIN, !prev);
      return !prev;
    });
  }, [isAdminPage, navigate]);

  // --- Build SidebarApp[] from catalog ------------------------------
  const sidebarApps: SidebarApp[] = useMemo(() => {
    if (!catalog) return [];
    const cmp = (a: string, b: string) => a.localeCompare(b);
    return [...catalog]
      .sort((a: any, b: any) => cmp(a.appId, b.appId))
      .map((app: any) => ({
        id: app.appId,
        name: app.appId,
        health: app.health as 'live' | 'stale' | 'dead',
        exchangeCount: app.exchangeCount,
        routes: [...(app.routes || [])]
          .sort((a: any, b: any) => cmp(a.routeId, b.routeId))
          .map((r: any) => ({
            id: r.routeId,
            name: r.routeId,
            exchangeCount: r.exchangeCount,
            routeState: r.routeState ?? undefined,
          })),
        agents: [],
      }));
  }, [catalog]);

  // --- Tree nodes ---------------------------------------------------
  const appTreeNodes: SidebarTreeNode[] = useMemo(
    () => buildAppTreeNodes(sidebarApps, makeStatusDot, makeChevron, makeStopIcon, makePauseIcon),
    [sidebarApps],
  );

  const adminTreeNodes: SidebarTreeNode[] = useMemo(
    () => buildAdminTreeNodes(),
    [],
  );

  // --- Starred items ------------------------------------------------
  const starredItems = useMemo(
    () => collectStarredItems(sidebarApps, starredIds),
    [sidebarApps, starredIds],
  );

  // --- Reveal path for SidebarTree auto-expand ----------------------
  const sidebarRevealPath = (location.state as any)?.sidebarReveal ?? null;

  useEffect(() => {
    if (!sidebarRevealPath) return;
    if (sidebarRevealPath.startsWith('/apps') && !appsOpen) setAppsOpen(true);
    if (sidebarRevealPath.startsWith('/admin') && !adminOpen) setAdminOpen(true);
  }, [sidebarRevealPath]); // eslint-disable-line react-hooks/exhaustive-deps

  const effectiveSelectedPath = sidebarRevealPath ?? location.pathname;

  // --- Exchange full-text search via command palette -----------------
  const [paletteQuery, setPaletteQuery] = useState('');
  const debouncedQuery = useDebouncedValue(paletteQuery, 300);
  const { data: exchangeResults } = useSearchExecutions(
    {
      text: debouncedQuery || undefined,
      applicationId: scope.appId || undefined,
      routeId: scope.routeId || undefined,
      offset: 0,
      limit: 10,
    },
    false,
  );

  const catalogData = useMemo(
    () => buildSearchData(catalog, agents as any[], attributeKeys),
    [catalog, agents, attributeKeys],
  );

  const catalogRef = useRef(catalogData);
  if (catalogData !== catalogRef.current && JSON.stringify(catalogData) !== JSON.stringify(catalogRef.current)) {
    catalogRef.current = catalogData;
  }

  const adminSearchData: SearchResult[] = useMemo(
    () => buildAdminSearchData(adminUsers, adminGroups, adminRoles),
    [adminUsers, adminGroups, adminRoles],
  );

  const operationalSearchData: SearchResult[] = useMemo(() => {
    if (isAdminPage) return [];

    const exchangeItems: SearchResult[] = (exchangeResults?.data || []).map((e: any) => ({
      id: e.executionId,
      category: 'exchange' as const,
      title: e.executionId,
      badges: [{ label: e.status, color: statusToColor(e.status) }],
      meta: `${e.routeId} · ${e.applicationId ?? ''} · ${formatDuration(e.durationMs)}`,
      path: `/exchanges/${e.applicationId ?? ''}/${e.routeId}/${e.executionId}`,
      serverFiltered: true,
      matchContext: e.highlight ?? undefined,
    }));

    const attributeItems: SearchResult[] = [];
    if (debouncedQuery) {
      const q = debouncedQuery.toLowerCase();
      for (const e of exchangeResults?.data || []) {
        if (!e.attributes) continue;
        for (const [key, value] of Object.entries(e.attributes as Record<string, string>)) {
          if (key.toLowerCase().includes(q) || String(value).toLowerCase().includes(q)) {
            attributeItems.push({
              id: `${e.executionId}-attr-${key}`,
              category: 'attribute' as const,
              title: `${key} = "${value}"`,
              badges: [{ label: e.status, color: statusToColor(e.status) }],
              meta: `${e.executionId} · ${e.routeId} · ${e.applicationId ?? ''}`,
              path: `/exchanges/${e.applicationId ?? ''}/${e.routeId}/${e.executionId}`,
              serverFiltered: true,
            });
          }
        }
      }
    }

    return [...catalogRef.current, ...exchangeItems, ...attributeItems];
  }, [isAdminPage, catalogRef.current, exchangeResults, debouncedQuery]);

  const searchData = isAdminPage ? adminSearchData : operationalSearchData;

  // --- Breadcrumb ---------------------------------------------------
  const breadcrumb = useMemo(() => {
    if (isAdminPage) {
      const LABELS: Record<string, string> = {
        admin: 'Admin',
        rbac: 'Users & Roles',
        audit: 'Audit Log',
        oidc: 'OIDC',
        database: 'Database',
        clickhouse: 'ClickHouse',
        appconfig: 'App Config',
      };
      const parts = location.pathname.split('/').filter(Boolean);
      return parts.map((part, i) => ({
        label: LABELS[part] ?? part,
        ...(i < parts.length - 1 ? { href: '/' + parts.slice(0, i + 1).join('/') } : {}),
      }));
    }
    const items: { label: string; href?: string }[] = [
      { label: 'All Applications', href: `/${scope.tab}` },
    ];
    if (scope.appId) {
      items.push({ label: scope.appId, href: `/${scope.tab}/${scope.appId}` });
    }
    if (scope.routeId) {
      items.push({ label: scope.routeId });
    }
    if (items.length > 0 && !scope.routeId && !scope.appId) {
      delete items[items.length - 1].href;
    }
    return items;
  }, [location.pathname, isAdminPage, scope.tab, scope.appId, scope.routeId]);

  // --- Callbacks ----------------------------------------------------
  const handleLogout = useCallback(() => {
    logout();
    useEnvironmentStore.getState().setEnvironment(undefined);
    navigate('/login');
  }, [logout, navigate]);

  const ADMIN_CATEGORIES = new Set(['user', 'group', 'role']);
  const ADMIN_TAB_MAP: Record<string, string> = { user: 'users', group: 'groups', role: 'roles' };

  const handlePaletteSelect = useCallback((result: any) => {
    if (result.path) {
      if (ADMIN_CATEGORIES.has(result.category)) {
        const itemId = result.id.split(':').slice(1).join(':');
        navigate(result.path, {
          state: { tab: ADMIN_TAB_MAP[result.category], highlight: itemId },
        });
      } else {
        const state: Record<string, unknown> = { sidebarReveal: result.path };
        if (result.category === 'exchange' || result.category === 'attribute') {
          const parts = result.path.split('/').filter(Boolean);
          if (parts.length === 4 && parts[0] === 'exchanges') {
            state.selectedExchange = {
              executionId: parts[3],
              applicationId: parts[1],
              routeId: parts[2],
            };
          }
        }
        navigate(result.path, { state });
      }
    }
    setPaletteOpen(false);
  }, [navigate, setPaletteOpen]);

  const handlePaletteSubmit = useCallback((query: string) => {
    if (isAdminPage) {
      // Find first matching admin result and navigate to it
      const q = query.toLowerCase();
      const match = adminSearchData.find(
        (r) => r.title.toLowerCase().includes(q) || r.meta.toLowerCase().includes(q),
      );
      if (match) {
        handlePaletteSelect(match);
      } else {
        navigate('/admin/rbac');
      }
    } else {
      const baseParts = ['/exchanges'];
      if (scope.appId) baseParts.push(scope.appId);
      if (scope.routeId) baseParts.push(scope.routeId);
      navigate(`${baseParts.join('/')}?text=${encodeURIComponent(query)}`);
    }
  }, [isAdminPage, adminSearchData, handlePaletteSelect, navigate, scope.appId, scope.routeId]);

  const handleSidebarNavigate = useCallback((path: string) => {
    const state = { sidebarReveal: path };

    // When not auto-refreshing, treat navigation as a manual refresh
    if (!autoRefresh) {
      refreshTimeRange();
      queryClient.invalidateQueries();
    }

    const appMatch = path.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
    if (appMatch) {
      const [, sAppId, sRouteId] = appMatch;
      if (scope.tab === 'config') {
        // Config tab: always navigate to /config/:appId (route click → same app config)
        navigate(`/config/${sAppId}`, { state });
      } else {
        navigate(sRouteId ? `/${scope.tab}/${sAppId}/${sRouteId}` : `/${scope.tab}/${sAppId}`, { state });
      }
      return;
    }

    const agentMatch = path.match(/^\/agents\/([^/]+)(?:\/(.+))?$/);
    if (agentMatch) {
      const [, sAppId, sInstanceId] = agentMatch;
      navigate(sInstanceId ? `/runtime/${sAppId}/${sInstanceId}` : `/runtime/${sAppId}`, { state });
      return;
    }

    navigate(path, { state });
  }, [navigate, scope.tab, autoRefresh, refreshTimeRange, queryClient]);

  // --- Render -------------------------------------------------------
  const camelLogo = (
    <img src={sidebarLogo} alt="" style={{ width: 28, height: 28 }} />
  );

  const sidebarElement = (
    <Sidebar
      collapsed={sidebarCollapsed}
      onCollapseToggle={handleCollapseToggle}
      searchValue={filterQuery}
      onSearchChange={setFilterQuery}
    >
      <Sidebar.Header
        logo={camelLogo}
        title="Cameleer"
        version={__APP_VERSION__}
      />

      {/* Applications section */}
      <Sidebar.Section
        icon={createElement(Box, { size: 16 })}
        label="Applications"
        open={appsOpen}
        onToggle={toggleApps}
      >
        <SidebarTree
          nodes={appTreeNodes}
          selectedPath={effectiveSelectedPath}
          isStarred={isStarred}
          onToggleStar={toggleStar}
          filterQuery={filterQuery}
          persistKey="apps"
          autoRevealPath={sidebarRevealPath}
          onNavigate={handleSidebarNavigate}
        />
      </Sidebar.Section>

      {/* Starred section — only when there are starred items */}
      {starredItems.length > 0 && (
        <Sidebar.Section
          icon={createElement(Star, { size: 16 })}
          label="Starred"
          open={starredOpen}
          onToggle={() => setStarredOpen((v) => !v)}
        >
          <StarredList
            items={starredItems}
            onNavigate={handleSidebarNavigate}
            onRemove={toggleStar}
          />
        </Sidebar.Section>
      )}

      {/* Admin section — only visible to ADMIN role */}
      {isAdmin && (
        <Sidebar.Section
          icon={createElement(Settings, { size: 16 })}
          label="Admin"
          open={adminOpen}
          onToggle={toggleAdmin}
          active={isAdminPage}
        >
          <SidebarTree
            nodes={adminTreeNodes}
            selectedPath={location.pathname}
            isStarred={isStarred}
            onToggleStar={toggleStar}
            filterQuery={filterQuery}
            persistKey="admin"
            autoRevealPath={sidebarRevealPath}
            onNavigate={handleSidebarNavigate}
          />
        </Sidebar.Section>
      )}

      {/* Footer */}
      <Sidebar.Footer>
        <Sidebar.FooterLink
          icon={createElement(FileText, { size: 16 })}
          label="API Docs"
          active={location.pathname === '/api-docs'}
          onClick={() => handleSidebarNavigate('/api-docs')}
        />
      </Sidebar.Footer>
    </Sidebar>
  );

  return (
    <AppShell sidebar={sidebarElement}>
      <TopBar
        breadcrumb={breadcrumb}
        environment={
          <EnvironmentSelector
            environments={environments}
            value={selectedEnv}
            onChange={setSelectedEnv}
          />
        }
        user={username ? { name: username } : undefined}
        onLogout={handleLogout}
      />
      <CommandPalette
        key={isAdminPage ? 'admin' : 'ops'}
        open={paletteOpen}
        onClose={() => setPaletteOpen(false)}
        onOpen={() => setPaletteOpen(true)}
        onSelect={handlePaletteSelect}
        onSubmit={handlePaletteSubmit}
        onQueryChange={setPaletteQuery}
        data={searchData}
      />

      {!isAdminPage && (
        <ContentTabs active={scope.tab} onChange={setTab} scope={scope} />
      )}

      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minHeight: 0 }}>
        <Outlet key={selectedEnv ?? '__all__'} />
      </main>
    </AppShell>
  );
}

export function LayoutShell() {
  return (
    <ToastProvider>
      <CommandPaletteProvider>
        <GlobalFilterProvider>
          <BreadcrumbProvider>
            <LayoutContent />
          </BreadcrumbProvider>
        </GlobalFilterProvider>
      </CommandPaletteProvider>
    </ToastProvider>
  );
}
