import { Outlet, useNavigate, useLocation } from 'react-router';
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 { Box, Cpu, GitBranch, Settings, FileText, ChevronRight } from 'lucide-react';
import { useRouteCatalog } from '../api/queries/catalog';
import { useAgents } from '../api/queries/agents';
import { useSearchExecutions, useAttributeKeys } from '../api/queries/executions';
import { useAuthStore } from '../auth/auth-store';
import { useState, useMemo, useCallback, useEffect, useRef, createElement } from 'react';
import { ContentTabs } from './ContentTabs';
import { useScope } from '../hooks/useScope';
import {
  buildAppTreeNodes,
  buildAgentTreeNodes,
  buildRouteTreeNodes,
  buildAdminTreeNodes,
  readCollapsed,
  writeCollapsed,
} from './sidebar-utils';
import type { SidebarApp } from './sidebar-utils';

/* ------------------------------------------------------------------ */
/*  Search data builder (unchanged)                                   */
/* ------------------------------------------------------------------ */

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 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 for tree builders                                  */
/* ------------------------------------------------------------------ */

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

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

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

const SK_APPS = 'sidebar:section:apps';
const SK_AGENTS = 'sidebar:section:agents';
const SK_ROUTES = 'sidebar:section:routes';
const SK_ADMIN = 'sidebar:section:admin';
const SK_COLLAPSED = 'sidebar:collapsed';

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

function LayoutContent() {
  const navigate = useNavigate();
  const location = useLocation();
  const { timeRange } = useGlobalFilters();
  const { data: catalog } = useRouteCatalog(timeRange.start.toISOString(), timeRange.end.toISOString());
  const { data: agents } = useAgents();
  const { data: attributeKeys } = useAttributeKeys();
  const { username, logout } = useAuthStore();
  const { open: paletteOpen, setOpen: setPaletteOpen } = useCommandPalette();
  const { scope, setTab } = useScope();

  // --- Starred items ------------------------------------------------
  const { 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('');

  // --- Section open states ------------------------------------------
  const isAdminPage = location.pathname.startsWith('/admin');
  const [appsOpen, setAppsOpen] = useState(() => isAdminPage ? false : readCollapsed(SK_APPS, true));
  const [agentsOpen, setAgentsOpen] = useState(() => isAdminPage ? false : readCollapsed(SK_AGENTS, false));
  const [routesOpen, setRoutesOpen] = useState(() => isAdminPage ? false : readCollapsed(SK_ROUTES, false));
  const [adminOpen, setAdminOpen] = useState(() => isAdminPage ? true : readCollapsed(SK_ADMIN, false));

  // Ref to remember operational section states when switching to admin
  const opsStateRef = useRef({ apps: appsOpen, agents: agentsOpen, routes: routesOpen });

  // Accordion effect: when entering admin, collapse operational sections; when leaving, restore
  const prevAdminRef = useRef(isAdminPage);
  useEffect(() => {
    if (isAdminPage && !prevAdminRef.current) {
      // Entering admin — save operational states and collapse them
      opsStateRef.current = { apps: appsOpen, agents: agentsOpen, routes: routesOpen };
      setAppsOpen(false);
      setAgentsOpen(false);
      setRoutesOpen(false);
      setAdminOpen(true);
      writeCollapsed(SK_APPS, false);
      writeCollapsed(SK_AGENTS, false);
      writeCollapsed(SK_ADMIN, true);
    } else if (!isAdminPage && prevAdminRef.current) {
      // Leaving admin — restore operational states
      setAppsOpen(opsStateRef.current.apps);
      setAgentsOpen(opsStateRef.current.agents);
      setRoutesOpen(opsStateRef.current.routes);
      setAdminOpen(false);
      writeCollapsed(SK_APPS, opsStateRef.current.apps);
      writeCollapsed(SK_AGENTS, opsStateRef.current.agents);
      writeCollapsed(SK_ROUTES, opsStateRef.current.routes);
      writeCollapsed(SK_ADMIN, false);
    }
    prevAdminRef.current = isAdminPage;
  }, [isAdminPage]); // eslint-disable-line react-hooks/exhaustive-deps

  const toggleApps = useCallback(() => {
    if (isAdminPage) {
      // Clicking operational section while in admin navigates away
      navigate('/exchanges');
      return;
    }
    setAppsOpen((prev) => {
      writeCollapsed(SK_APPS, !prev);
      return !prev;
    });
  }, [isAdminPage, navigate]);

  const toggleAgents = useCallback(() => {
    if (isAdminPage) {
      navigate('/exchanges');
      return;
    }
    setAgentsOpen((prev) => {
      writeCollapsed(SK_AGENTS, !prev);
      return !prev;
    });
  }, [isAdminPage, navigate]);

  const toggleRoutes = useCallback(() => {
    if (isAdminPage) {
      navigate('/exchanges');
      return;
    }
    setRoutesOpen((prev) => {
      writeCollapsed(SK_ROUTES, !prev);
      return !prev;
    });
  }, [isAdminPage, navigate]);

  const toggleAdmin = useCallback(() => {
    setAdminOpen((prev) => {
      writeCollapsed(SK_ADMIN, !prev);
      return !prev;
    });
  }, []);

  // --- 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,
          })),
        agents: [...(app.agents || [])]
          .sort((a: any, b: any) => cmp(a.name, b.name))
          .map((a: any) => ({
            id: a.id,
            name: a.name,
            status: a.status as 'live' | 'stale' | 'dead',
            tps: a.tps,
          })),
      }));
  }, [catalog]);

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

  const agentTreeNodes: SidebarTreeNode[] = useMemo(
    () => buildAgentTreeNodes(sidebarApps, makeStatusDot),
    [sidebarApps],
  );

  const routeTreeNodes: SidebarTreeNode[] = useMemo(
    () => buildRouteTreeNodes(sidebarApps, makeStatusDot, makeChevron),
    [sidebarApps],
  );

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

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

  // --- 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],
  );

  // Stable reference for catalog data
  const catalogRef = useRef(catalogData);
  if (catalogData !== catalogRef.current && JSON.stringify(catalogData) !== JSON.stringify(catalogRef.current)) {
    catalogRef.current = catalogData;
  }

  const searchData: SearchResult[] = useMemo(() => {
    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];
  }, [catalogRef.current, exchangeResults, debouncedQuery]);

  // --- 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();
    navigate('/login');
  }, [logout, navigate]);

  const handlePaletteSelect = useCallback((result: any) => {
    if (result.path) {
      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) => {
    const baseParts = ['/exchanges'];
    if (scope.appId) baseParts.push(scope.appId);
    if (scope.routeId) baseParts.push(scope.routeId);
    navigate(`${baseParts.join('/')}?text=${encodeURIComponent(query)}`);
  }, [navigate, scope.appId, scope.routeId]);

  // Translate Sidebar's internal paths to our URL structure.
  const handleSidebarNavigate = useCallback((path: string) => {
    const state = { sidebarReveal: path };

    // /apps/:appId and /apps/:appId/:routeId -> current tab
    const appMatch = path.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
    if (appMatch) {
      const [, sAppId, sRouteId] = appMatch;
      navigate(sRouteId ? `/${scope.tab}/${sAppId}/${sRouteId}` : `/${scope.tab}/${sAppId}`, { state });
      return;
    }

    // /agents/:appId/:instanceId -> runtime tab
    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]);

  // --- Render -------------------------------------------------------
  const sidebarElement = (
    <Sidebar
      collapsed={sidebarCollapsed}
      onCollapseToggle={handleCollapseToggle}
      searchValue={filterQuery}
      onSearchChange={setFilterQuery}
    >
      <Sidebar.Header
        logo={createElement(Box, { size: 20 })}
        title="Cameleer"
      />

      {/* When on admin pages, show Admin section first (expanded) */}
      {isAdminPage && (
        <Sidebar.Section
          icon={createElement(Settings, { size: 16 })}
          label="Admin"
          open={adminOpen}
          onToggle={toggleAdmin}
          active
        >
          <SidebarTree
            nodes={adminTreeNodes}
            selectedPath={location.pathname}
            isStarred={isStarred}
            onToggleStar={toggleStar}
            filterQuery={filterQuery}
            persistKey="admin"
            autoRevealPath={sidebarRevealPath}
            onNavigate={handleSidebarNavigate}
          />
        </Sidebar.Section>
      )}

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

      <Sidebar.Section
        icon={createElement(Cpu, { size: 16 })}
        label="Agents"
        open={agentsOpen}
        onToggle={toggleAgents}
      >
        <SidebarTree
          nodes={agentTreeNodes}
          selectedPath={sidebarRevealPath ?? location.pathname}
          isStarred={isStarred}
          onToggleStar={toggleStar}
          filterQuery={filterQuery}
          persistKey="agents"
          autoRevealPath={sidebarRevealPath}
          onNavigate={handleSidebarNavigate}
        />
      </Sidebar.Section>

      <Sidebar.Section
        icon={createElement(GitBranch, { size: 16 })}
        label="Routes"
        open={routesOpen}
        onToggle={toggleRoutes}
      >
        <SidebarTree
          nodes={routeTreeNodes}
          selectedPath={sidebarRevealPath ?? location.pathname}
          isStarred={isStarred}
          onToggleStar={toggleStar}
          filterQuery={filterQuery}
          persistKey="routes"
          autoRevealPath={sidebarRevealPath}
          onNavigate={handleSidebarNavigate}
        />
      </Sidebar.Section>

      {/* When NOT on admin pages, show Admin section at bottom */}
      {!isAdminPage && (
        <Sidebar.Section
          icon={createElement(Settings, { size: 16 })}
          label="Admin"
          open={adminOpen}
          onToggle={toggleAdmin}
        >
          <SidebarTree
            nodes={adminTreeNodes}
            selectedPath={location.pathname}
            isStarred={isStarred}
            onToggleStar={toggleStar}
            filterQuery={filterQuery}
            persistKey="admin"
            autoRevealPath={sidebarRevealPath}
            onNavigate={handleSidebarNavigate}
          />
        </Sidebar.Section>
      )}

      <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}
        user={username ? { name: username } : undefined}
        onLogout={handleLogout}
      />
      <CommandPalette
        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 />
      </main>
    </AppShell>
  );
}

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