import { Outlet, useNavigate, useLocation } from 'react-router';
import { AppShell, Sidebar, TopBar, CommandPalette, CommandPaletteProvider, GlobalFilterProvider, ToastProvider, BreadcrumbProvider, useCommandPalette, useGlobalFilters } from '@cameleer/design-system';
import type { SidebarApp, SearchResult } from '@cameleer/design-system';
import { useRouteCatalog } from '../api/queries/catalog';
import { useAgents } from '../api/queries/agents';
import { useSearchExecutions } from '../api/queries/executions';
import { useAuthStore } from '../auth/auth-store';
import { useState, useMemo, useCallback, useEffect } from 'react';
import { ContentTabs } from './ContentTabs';
import { ScopeTrail } from './ScopeTrail';
import { useScope } from '../hooks/useScope';

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

function buildSearchData(
  catalog: any[] | undefined,
  agents: any[] | 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: healthToColor(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: 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.id,
        category: 'agent',
        title: agent.name,
        badges: [{ label: (agent.state || 'unknown').toUpperCase(), color: healthToColor((agent.state || '').toLowerCase()) }],
        meta: `${agent.application} · ${agent.version || ''}${agent.agentTps != null ? ` · ${agent.agentTps.toFixed(1)} msg/s` : ''}`,
        path: `/runtime/${agent.application}/${agent.id}`,
      });
    }
  }

  return results;
}

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;
}

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 { username, logout } = useAuthStore();
  const { open: paletteOpen, setOpen: setPaletteOpen } = useCommandPalette();
  const { scope, setTab } = useScope();

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

  const sidebarApps: SidebarApp[] = useMemo(() => {
    if (!catalog) return [];
    return catalog.map((app: any) => ({
      id: app.appId,
      name: app.appId,
      health: app.health as 'live' | 'stale' | 'dead',
      exchangeCount: app.exchangeCount,
      routes: (app.routes || []).map((r: any) => ({
        id: r.routeId,
        name: r.routeId,
        exchangeCount: r.exchangeCount,
      })),
      agents: [],
    }));
  }, [catalog]);

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

  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.applicationName ?? ''} · ${formatDuration(e.durationMs)}`,
      path: `/exchanges/${e.applicationName ?? ''}/${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.applicationName ?? ''}`,
              path: `/exchanges/${e.applicationName ?? ''}/${e.routeId}/${e.executionId}`,
              serverFiltered: true,
            });
          }
        }
      }
    }

    return [...catalogData, ...exchangeItems, ...attributeItems];
  }, [catalogData, exchangeResults, debouncedQuery]);

  const isAdminPage = location.pathname.startsWith('/admin');
  const breadcrumb = useMemo(() => {
    if (!isAdminPage) return [];
    const LABELS: Record<string, string> = {
      admin: 'Admin',
      rbac: 'Users & Roles',
      audit: 'Audit Log',
      oidc: 'OIDC',
      database: 'Database',
      opensearch: 'OpenSearch',
      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('/') } : {}),
    }));
  }, [location.pathname, isAdminPage]);

  const handleLogout = useCallback(() => {
    logout();
    navigate('/login');
  }, [logout, navigate]);

  const handlePaletteSelect = useCallback((result: any) => {
    if (result.path) {
      navigate(result.path, { state: result.path ? { sidebarReveal: result.path } : undefined });
    }
    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]);

  // Intercept Sidebar's internal <Link> navigation to re-route through current tab
  const handleSidebarClick = useCallback((e: React.MouseEvent) => {
    const anchor = (e.target as HTMLElement).closest('a[href]');
    if (!anchor) return;
    const href = anchor.getAttribute('href') || '';

    // Intercept /apps/:appId and /apps/:appId/:routeId links
    const appMatch = href.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
    if (appMatch) {
      e.preventDefault();
      const [, sAppId, sRouteId] = appMatch;
      navigate(sRouteId ? `/${scope.tab}/${sAppId}/${sRouteId}` : `/${scope.tab}/${sAppId}`);
      return;
    }

    // Intercept /agents/* links — redirect to runtime tab
    const agentMatch = href.match(/^\/agents\/([^/]+)(?:\/(.+))?$/);
    if (agentMatch) {
      e.preventDefault();
      const [, sAppId, sInstanceId] = agentMatch;
      navigate(sInstanceId ? `/runtime/${sAppId}/${sInstanceId}` : `/runtime/${sAppId}`);
    }
  }, [navigate, scope.tab]);

  return (
    <AppShell
      sidebar={
        <div onClick={handleSidebarClick} style={{ height: '100%' }}>
          <Sidebar apps={sidebarApps} />
        </div>
      }
    >
      <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} />
          <div style={{ padding: '0 1.5rem', paddingTop: '0.5rem' }}>
            <ScopeTrail scope={scope} onNavigate={(path) => navigate(path)} />
          </div>
        </>
      )}

      <main style={{ flex: 1, overflow: 'auto', padding: isAdminPage ? '1.5rem' : '0.75rem 1.5rem' }}>
        <Outlet />
      </main>
    </AppShell>
  );
}

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