import { useState, useEffect, type ReactNode } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import Prism from 'prismjs';
import { type Page, routeMap, pathToPage } from '../App';
import { useDownload } from '../contexts/DownloadContext';
import { useAutoTestRunner } from '../contexts/AutoTestRunnerContext';
import { useAccuracyRunner } from '../contexts/AccuracyRunnerContext';
import { usePlayground } from '../contexts/PlaygroundContext';
import { useTheme } from '../contexts/ThemeContext';
import { api } from '../services/api';
import { TRIAL_PAUSE_MS } from '../services/autoTestRunner';

interface LayoutProps {
  children: ReactNode;
}

interface MenuCategory {
  id: string;
  label: string;
  items?: MenuItem[];
  subcategories?: MenuCategory[];
}

interface MenuItem {
  page: Page;
  label: string;
  hash?: string;
  children?: MenuItem[];
}

const menuStructure: MenuCategory[] = [
  {
    id: 'apps',
    label: 'Apps',
    items: [
      { page: 'chat', label: 'Chat' },
      { page: 'vram-calculator', label: 'VRAM Calculator' },
      { page: 'translator', label: 'Translator' },
    ],
  },
  {
    id: 'running',
    label: 'Running',
    items: [{ page: 'model-ps', label: 'Models' }],
  },
  {
    id: 'kronk',
    label: 'Kronk',
    subcategories: [
      {
        id: 'model',
        label: 'Models',
        items: [
          { page: 'model-list', label: 'List' },
          { page: 'model-pull', label: 'HF Pull' },
          { page: 'kms-pull', label: 'KMS Pull' },
        ],
      },
      {
        id: 'catalog',
        label: 'Catalog',
        items: [
          { page: 'catalog-list', label: 'List' },
        ],
      },
      {
        id: 'libs',
        label: 'Libs',
        items: [{ page: 'libs-pull', label: 'Manage' }],
      },
    ],
  },
  {
    id: 'bucky',
    label: 'Bucky',
    subcategories: [
      {
        id: 'bucky-model',
        label: 'Models',
        items: [{ page: 'bucky-model-list', label: 'List' }],
      },
      {
        id: 'bucky-libs',
        label: 'Libs',
        items: [{ page: 'bucky-libs', label: 'Manage' }],
      },
    ],
  },
  {
    id: 'security',
    label: 'Security',
    subcategories: [
      {
        id: 'security-key',
        label: 'Key',
        items: [
          { page: 'security-key-list', label: 'List' },
          { page: 'security-key-create', label: 'Create' },
          { page: 'security-key-delete', label: 'Delete' },
        ],
      },
      {
        id: 'security-token',
        label: 'Token',
        items: [
          { page: 'security-token-create', label: 'Create' },
          { page: 'settings', label: 'Apply' },
        ],
      },
    ],
  },
  {
    id: 'testing',
    label: 'Testing',
    items: [
      { page: 'accuracy', label: 'Accuracy' },
      { page: 'testing-basic', label: 'Basic' },
      { page: 'testing-sampling', label: 'Sampling' },
      { page: 'testing-configurator', label: 'Configuration' },
    ],
  },
  {
    id: 'docs',
    label: 'Docs',
    subcategories: [
      {
        id: 'docs-manual-sub',
        label: 'Manual',
        items: [
          { page: 'docs-manual', label: 'Introduction', hash: 'chapter-1-introduction' },
          { page: 'docs-manual', label: 'Installation & Quick Start', hash: 'chapter-2-installation-quick-start' },
          { page: 'docs-manual', label: 'Model Configuration', hash: 'chapter-3-model-configuration' },
          { page: 'docs-manual', label: 'Batch Processing', hash: 'chapter-4-batch-processing' },
          { page: 'docs-manual', label: 'Message Caching', hash: 'chapter-5-message-caching' },
          { page: 'docs-manual', label: 'Speculative Decoding & MTP', hash: 'chapter-6-speculative-decoding-mtp' },
          { page: 'docs-manual', label: 'YaRN Extended Context', hash: 'chapter-7-yarn-extended-context' },
          { page: 'docs-manual', label: 'Model Server', hash: 'chapter-8-model-server' },
          { page: 'docs-manual', label: 'API Endpoints', hash: 'chapter-9-api-endpoints' },
          { page: 'docs-manual', label: 'Request Parameters', hash: 'chapter-10-request-parameters' },
          { page: 'docs-manual', label: 'Multi-Modal Models', hash: 'chapter-11-multi-modal-models' },
          { page: 'docs-manual', label: 'Security & Authentication', hash: 'chapter-12-security-authentication' },
          { page: 'docs-manual', label: 'Browser UI (BUI)', hash: 'chapter-13-browser-ui-bui' },
          { page: 'docs-manual', label: 'Client Integration', hash: 'chapter-14-client-integration' },
          { page: 'docs-manual', label: 'Observability', hash: 'chapter-15-observability' },
          { page: 'docs-manual', label: 'MCP Service', hash: 'chapter-16-mcp-service' },
          { page: 'docs-manual', label: 'Troubleshooting', hash: 'chapter-17-troubleshooting' },
          { page: 'docs-manual', label: 'Bucky (Audio Transcription)', hash: 'chapter-18-bucky' },
          { page: 'docs-manual', label: 'Developer Guide', hash: 'chapter-19-developer-guide' },
        ],
      },
      {
        id: 'docs-sdk',
        label: 'SDK',
        items: [
          { page: 'docs-sdk-kronk', label: 'Kronk' },
          { page: 'docs-sdk-model', label: 'Model' },
          { page: 'docs-sdk-pool', label: 'Pool' },
          { page: 'docs-sdk-bucky', label: 'Bucky' },
          { page: 'docs-sdk-bucky-model', label: 'Bucky Model' },
          { page: 'docs-sdk-examples', label: 'Examples', children: [
            { page: 'docs-sdk-examples', label: 'Agent', hash: 'example-agent' },
            { page: 'docs-sdk-examples', label: 'Audio', hash: 'example-audio' },
            { page: 'docs-sdk-examples', label: 'Bucky', hash: 'example-bucky' },
            { page: 'docs-sdk-examples', label: 'Bucky-Diar', hash: 'example-bucky-diar' },
            { page: 'docs-sdk-examples', label: 'Bucky-Stream', hash: 'example-bucky-stream' },
            { page: 'docs-sdk-examples', label: 'Chat', hash: 'example-chat' },
            { page: 'docs-sdk-examples', label: 'Concurrency', hash: 'example-concurrency' },
            { page: 'docs-sdk-examples', label: 'Embedding', hash: 'example-embedding' },
            { page: 'docs-sdk-examples', label: 'Grammar', hash: 'example-grammar' },
            { page: 'docs-sdk-examples', label: 'Pool', hash: 'example-pool' },
            { page: 'docs-sdk-examples', label: 'Question', hash: 'example-question' },
            { page: 'docs-sdk-examples', label: 'Rag', hash: 'example-rag' },
            { page: 'docs-sdk-examples', label: 'Rerank', hash: 'example-rerank' },
            { page: 'docs-sdk-examples', label: 'Response', hash: 'example-response' },
            { page: 'docs-sdk-examples', label: 'Vision', hash: 'example-vision' },
          ] },
        ],
      },
      {
        id: 'docs-cli-sub',
        label: 'CLI',
        items: [
          { page: 'docs-cli-catalog', label: 'catalog' },
          { page: 'docs-cli-libs', label: 'libs' },
          { page: 'docs-cli-model', label: 'model' },
          { page: 'docs-cli-run', label: 'run' },
          { page: 'docs-cli-security', label: 'security' },
          { page: 'docs-cli-server', label: 'server' },
        ],
      },
      {
        id: 'docs-api-sub',
        label: 'Web API',
        items: [
          { page: 'docs-api-chat', label: 'Chat' },
          { page: 'docs-api-messages', label: 'Messages' },
          { page: 'docs-api-responses', label: 'Responses' },
          { page: 'docs-api-embeddings', label: 'Embeddings' },
          { page: 'docs-api-rerank', label: 'Rerank' },
          { page: 'docs-api-tokenize', label: 'Tokenize' },
          { page: 'docs-api-tools', label: 'Tools' },
        ],
      },
    ],
  },
];

const categoryIcons: Record<string, JSX.Element> = {
  running: (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="3 12 7 12 10 4 14 20 17 12 21 12" />
    </svg>
  ),
  kronk: (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 2l9 5v10l-9 5-9-5V7z" />
      <path d="M12 22V12" />
      <path d="M3 7l9 5 9-5" />
    </svg>
  ),
  bucky: (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="9" y="2" width="6" height="12" rx="3" />
      <path d="M5 11a7 7 0 0 0 14 0" />
      <line x1="12" y1="18" x2="12" y2="22" />
      <line x1="8" y1="22" x2="16" y2="22" />
    </svg>
  ),
  apps: (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="3" width="7" height="7" rx="1" />
      <rect x="14" y="3" width="7" height="7" rx="1" />
      <rect x="3" y="14" width="7" height="7" rx="1" />
      <rect x="14" y="14" width="7" height="7" rx="1" />
    </svg>
  ),
  security: (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
    </svg>
  ),
  docs: (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
      <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
    </svg>
  ),
  testing: (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M9 2h6" />
      <path d="M10 2v6.5L5 18a2 2 0 0 0 1.8 3h10.4A2 2 0 0 0 19 18l-5-9.5V2" />
      <path d="M7.5 14h9" />
    </svg>
  ),
};

export default function Layout({ children }: LayoutProps) {
  const location = useLocation();
  const navigate = useNavigate();
  const currentPage = pathToPage[location.pathname] || 'home';
  const [expandedCategories, setExpandedCategories] = useState<Set<string>>(new Set());
  const { download, isDownloading } = useDownload();
  const { run, isRunning: isAutoTesting, stopRun } = useAutoTestRunner();
  const {
    activeRun: accuracyRun,
    stopActive: stopAccuracy,
    completedRun: accuracyCompleted,
    dismissCompleted: dismissAccuracy,
  } = useAccuracyRunner();
  const { session, setSession, selectedModel, setChatMessages } = usePlayground();
  const { theme, toggleTheme } = useTheme();

  // Unload the active Basic session from the sidebar indicator.
  const [unloadingSession, setUnloadingSession] = useState(false);
  const handleUnloadSession = async () => {
    if (!session) return;
    setUnloadingSession(true);
    try {
      await api.deletePlaygroundSession(session.session_id);
      setSession(null);
      setChatMessages([]);
    } catch {
      // Best effort — clear local state even if the server call fails so the
      // indicator doesn't get stuck.
      setSession(null);
    } finally {
      setUnloadingSession(false);
    }
  };

  const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
    try {
      return localStorage.getItem('sidebar-collapsed') === 'true';
    } catch {
      return false;
    }
  });

  const toggleSidebar = () => {
    setSidebarCollapsed(prev => {
      const next = !prev;
      try { localStorage.setItem('sidebar-collapsed', String(next)); } catch {}
      return next;
    });
  };

  const getFirstPage = (category: MenuCategory): string | null => {
    if (category.items && category.items.length > 0) {
      const first = category.items[0];
      const path = routeMap[first.page];
      return first.hash ? `${path}#${first.hash}` : path;
    }
    if (category.subcategories) {
      for (const sub of category.subcategories) {
        const page = getFirstPage(sub);
        if (page) return page;
      }
    }
    return null;
  };

  const autoTestTitle = (() => {
    if (!run) return '';
    if (isAutoTesting) return 'Testing...';
    switch (run.status) {
      case 'completed': return 'Completed';
      case 'cancelled': return 'Cancelled';
      case 'error': return 'Failed';
      default: return 'Testing';
    }
  })();

  const autoTestSubtitle = (() => {
    if (!run) return undefined;
    if (run.status === 'error' && run.errorMessage) return run.errorMessage;
    if (isAutoTesting && run.totalTrials === 0) {
      return run.calibrationStatus ?? run.templateRepairStatus ?? 'Preparing...';
    }
    if (run.totalTrials > 0) {
      return `Trial ${Math.min(run.currentTrialIndex + (isAutoTesting ? 1 : 0), run.totalTrials)}/${run.totalTrials}`;
    }
    return undefined;
  })();

  const autoTestLogLine = (() => {
    if (!run || !isAutoTesting) return undefined;
    const runningTrial = run.trials.find(t => t?.status === 'running');
    const entries = runningTrial?.logEntries;
    if (entries && entries.length > 0) return entries[entries.length - 1].message;
    return undefined;
  })();

  const autoTestEta = (() => {
    if (!run || !isAutoTesting || run.totalTrials === 0) return undefined;
    const completed = run.trials.filter(t => t?.startedAt && t?.finishedAt).length;
    if (completed === 0 || completed >= run.totalTrials) return undefined;
    const durations = run.trials
      .filter(t => t?.startedAt && t?.finishedAt)
      .map(t => Date.parse(t.finishedAt!) - Date.parse(t.startedAt!))
      .filter(ms => Number.isFinite(ms) && ms > 0);
    if (durations.length === 0) return undefined;
    const avgMs = durations.reduce((a, b) => a + b, 0) / durations.length;
    const remaining = Math.max(0, run.totalTrials - completed);
    const remainingMs = avgMs * remaining + TRIAL_PAUSE_MS * remaining;
    const sec = Math.round(remainingMs / 1000);
    if (sec < 60) return `ETA ~${sec}s`;
    const m = Math.floor(sec / 60);
    const s = sec % 60;
    if (m < 60) return `ETA ~${m}m ${s}s`;
    const h = Math.floor(m / 60);
    return `ETA ~${h}h ${m % 60}m`;
  })();

  const downloadEta = (() => {
    if (!download || !isDownloading || !download.progress) return undefined;
    const { totalBytes, currentBytes, mbPerSec, startedAtMs } = download.progress;
    const elapsedSec = (Date.now() - startedAtMs) / 1000;
    if (elapsedSec < 5 || totalBytes <= 0 || currentBytes >= totalBytes || mbPerSec <= 0) return undefined;
    const remainingBytes = totalBytes - currentBytes;
    const etaSec = remainingBytes / (mbPerSec * 1000 * 1000);
    if (etaSec < 60) return `ETA ~${Math.round(etaSec)}s`;
    const m = Math.floor(etaSec / 60);
    const s = Math.round(etaSec % 60);
    if (m < 60) return `ETA ~${m}m ${s}s`;
    const h = Math.floor(m / 60);
    return `ETA ~${h}h ${m % 60}m`;
  })();

  const showAutoTestIndicator = !!run;
  const showDownloadIndicator = !!download;
  const showAccuracyIndicator = !!accuracyRun || !!accuracyCompleted;
  const showSessionIndicator = !!session;

  const accuracyTitle = accuracyRun
    ? accuracyRun.mode === 'manual'
      ? 'Accuracy test...'
      : accuracyRun.mode === 'batch'
        ? 'Accuracy batch...'
        : 'Accuracy compare...'
    : '';

  // Auto-expand categories that contain the current page
  useEffect(() => {
    const findCategoryPath = (categories: MenuCategory[], targetPage: Page): string[] => {
      for (const category of categories) {
        if (category.items?.some((item) => item.page === targetPage)) {
          return [category.id];
        }
        if (category.subcategories) {
          const subPath = findCategoryPath(category.subcategories, targetPage);
          if (subPath.length > 0) {
            return [category.id, ...subPath];
          }
        }
      }
      return [];
    };

    const categoryPath = findCategoryPath(menuStructure, currentPage);

    // Find parent MenuItems (have children) whose page matches the current page.
    const itemKeys: string[] = [];
    const visitItems = (items?: MenuItem[]) => {
      if (!items) return;
      for (const it of items) {
        if (it.children && it.page === currentPage && !it.hash) {
          itemKeys.push(`menu-item:${it.page}`);
        }
        visitItems(it.children);
      }
    };
    const visitCategories = (cats: MenuCategory[]) => {
      for (const c of cats) {
        visitItems(c.items);
        if (c.subcategories) visitCategories(c.subcategories);
      }
    };
    visitCategories(menuStructure);

    if (categoryPath.length > 0 || itemKeys.length > 0) {
      setExpandedCategories((prev) => {
        const next = new Set(prev);
        categoryPath.forEach((id) => next.add(id));
        itemKeys.forEach((k) => next.add(k));
        return next;
      });
    }
  }, [currentPage]);

  // Syntax-highlight code blocks on every route change so Manual, SDK, CLI,
  // and API doc pages get GitHub-themed Prism colours.
  useEffect(() => {
    Prism.highlightAll();
  }, [currentPage]);

  const toggleCategory = (id: string) => {
    setExpandedCategories((prev) => {
      const next = new Set(prev);
      if (next.has(id)) {
        next.delete(id);
      } else {
        next.add(id);
      }
      return next;
    });
  };

  const isCategoryActive = (category: MenuCategory): boolean => {
    if (category.items) {
      return category.items.some((item) => item.page === currentPage);
    }
    if (category.subcategories) {
      return category.subcategories.some((sub) => isCategoryActive(sub));
    }
    return false;
  };

  const renderMenuItem = (item: MenuItem) => {
    const path = routeMap[item.page];
    const isActive = currentPage === item.page && !item.hash;

    if (item.children && item.children.length > 0) {
      const itemKey = `menu-item:${item.page}${item.hash ? `#${item.hash}` : ''}`;
      const isExpanded = expandedCategories.has(itemKey);
      const target = item.hash ? `${path}#${item.hash}` : path;

      const handleClick = (e: React.MouseEvent) => {
        e.preventDefault();
        toggleCategory(itemKey);
        if (!isActive) navigate(target);
      };

      return (
        <div key={itemKey} className="menu-item-group">
          <a
            href={target}
            onClick={handleClick}
            className={`menu-item menu-item-parent ${isActive ? 'active' : ''}`}
          >
            <span className="menu-item-label">{item.label}</span>
            <span className={`menu-category-arrow ${isExpanded ? 'expanded' : ''}`}>▶</span>
          </a>
          <div className={`menu-items menu-item-children ${isExpanded ? 'expanded' : ''}`}>
            {item.children.map(renderMenuItem)}
          </div>
        </div>
      );
    }

    if (item.hash) {
      const isHashActive = currentPage === item.page && location.hash === `#${item.hash}`;
      const handleClick = (e: React.MouseEvent) => {
        e.preventDefault();
        navigate(`${path}#${item.hash}`);
      };

      return (
        <a
          key={`${item.page}-${item.hash}`}
          href={`${path}#${item.hash}`}
          onClick={handleClick}
          className={`menu-item ${isHashActive ? 'active' : ''}`}
        >
          {item.label}
        </a>
      );
    }
    
    return (
      <Link
        key={item.page}
        to={path}
        className={`menu-item ${isActive ? 'active' : ''}`}
      >
        {item.label}
      </Link>
    );
  };

  const renderCategory = (category: MenuCategory, isSubmenu = false) => {
    const isExpanded = expandedCategories.has(category.id);
    const isActive = isCategoryActive(category);

    const handleHeaderClick = () => {
      if (sidebarCollapsed && !isSubmenu) {
        const firstPage = getFirstPage(category);
        if (firstPage) navigate(firstPage);
      } else {
        toggleCategory(category.id);
        if (!isExpanded) {
          const firstPage = getFirstPage(category);
          if (firstPage) navigate(firstPage);
        }
      }
    };

    return (
      <div key={category.id} className={`menu-category ${isSubmenu ? 'submenu' : ''}`}>
        <div
          className={`menu-category-header ${isActive ? 'active' : ''}`}
          onClick={handleHeaderClick}
          title={sidebarCollapsed ? category.label : undefined}
        >
          {!isSubmenu && categoryIcons[category.id] && (
            <span className="menu-category-icon" aria-hidden="true">
              {categoryIcons[category.id]}
            </span>
          )}
          <span className="menu-category-label">{category.label}</span>
          <span className={`menu-category-arrow ${isExpanded ? 'expanded' : ''}`}>▶</span>
        </div>
        <div className={`menu-items ${isExpanded ? 'expanded' : ''}`} {...(!isSubmenu && { 'data-label': category.label })}>
          {category.subcategories?.map((sub) => renderCategory(sub, true))}
          {category.items?.map(renderMenuItem)}
        </div>
      </div>
    );
  };

  return (
    <div className="app">
      <aside className={`sidebar ${sidebarCollapsed ? 'sidebar-collapsed' : ''}`}>
        <div className="sidebar-header">
          <Link to="/" style={{ textDecoration: 'none', color: 'inherit' }} className="sidebar-brand">
            <img src="/kronk-logo.png" alt="Kronk Logo" className="sidebar-logo" />
            <h1>Model Server</h1>
          </Link>
        </div>
        <nav>{menuStructure.map((category) => renderCategory(category))}</nav>
        {(showAutoTestIndicator || showDownloadIndicator || showAccuracyIndicator || showSessionIndicator) && (
          <div className="sidebar-indicators">
            {showSessionIndicator && (
              <div className="download-indicator">
                <div className="download-indicator-link autotest-indicator-link">
                  <Link to={routeMap['testing-basic']} className="autotest-indicator-top">
                    <div className="download-indicator-header">
                      <span className="download-indicator-icon success">●</span>
                      <span className="download-indicator-title">Session running</span>
                    </div>
                    <div className="download-indicator-url" title={selectedModel}>
                      {selectedModel ? selectedModel.split('/').pop() : 'Basic'}
                    </div>
                  </Link>
                  <button
                    type="button"
                    className="autotest-indicator-stop"
                    onClick={handleUnloadSession}
                    disabled={unloadingSession}
                    aria-label="Unload session"
                    title="Unload session"
                  >
                    {unloadingSession ? '…' : '⏏'}
                  </button>
                </div>
              </div>
            )}
            {showAutoTestIndicator && (
              <div className="download-indicator">
                <div className="download-indicator-link autotest-indicator-link">
                  <Link to={routeMap[run?.kind === 'config' ? 'testing-configurator' : 'testing-sampling']} className="autotest-indicator-top">
                    <div className="download-indicator-header">
                      {isAutoTesting ? (
                        <span className="download-indicator-spinner" />
                      ) : run.status === 'completed' ? (
                        <span className="download-indicator-icon success">✓</span>
                      ) : (
                        <span className="download-indicator-icon error">✗</span>
                      )}
                      <span className="download-indicator-title">{autoTestTitle}</span>
                    </div>
                    {autoTestSubtitle && (
                      <div className="download-indicator-url" title={autoTestSubtitle} aria-live="polite">
                        {autoTestSubtitle}
                      </div>
                    )}
                    {autoTestLogLine && (
                      <div className="autotest-indicator-log" title={autoTestLogLine}>
                        {autoTestLogLine}
                      </div>
                    )}
                    {autoTestEta && (
                      <div className="download-indicator-url" title={autoTestEta}>
                        {autoTestEta}
                      </div>
                    )}
                  </Link>
                  {isAutoTesting && (
                    <button type="button" className="autotest-indicator-stop" onClick={stopRun} aria-label="Stop automated testing" title="Stop automated testing">
                      ■
                    </button>
                  )}
                </div>
              </div>
            )}
            {showDownloadIndicator && (
              <div className="download-indicator">
                <Link to={download.origin === 'catalog' ? routeMap['kms-pull'] : routeMap['model-pull']} className="download-indicator-link">
                  <div className="download-indicator-header">
                    {isDownloading ? (
                      <span className="download-indicator-spinner" />
                    ) : download.status === 'complete' ? (
                      <span className="download-indicator-icon success">✓</span>
                    ) : (
                      <span className="download-indicator-icon error">✗</span>
                    )}
                    <span className="download-indicator-title">
                      {isDownloading ? 'Downloading...' : download.status === 'complete' ? 'Complete' : 'Failed'}
                    </span>
                  </div>
                  <div className="download-indicator-url" title={download.modelUrl}>
                    {download.modelUrl.split('/').pop()}
                  </div>
                  {downloadEta && (
                    <div className="download-indicator-url" title={downloadEta}>
                      {downloadEta}
                    </div>
                  )}
                </Link>
              </div>
            )}
            {showAccuracyIndicator && accuracyRun && (
              <div className="download-indicator">
                <div className="download-indicator-link autotest-indicator-link">
                  <Link to={routeMap['accuracy']} className="autotest-indicator-top">
                    <div className="download-indicator-header">
                      <span className="download-indicator-spinner" />
                      <span className="download-indicator-title">{accuracyTitle}</span>
                    </div>
                    <div className="download-indicator-url" title={accuracyRun.label}>
                      {accuracyRun.label}
                    </div>
                  </Link>
                  <button
                    type="button"
                    className="autotest-indicator-stop"
                    onClick={stopAccuracy}
                    aria-label="Stop accuracy run"
                    title="Stop accuracy run"
                  >
                    ■
                  </button>
                </div>
              </div>
            )}
            {showAccuracyIndicator && !accuracyRun && accuracyCompleted && (
              <div className="download-indicator">
                <div className="download-indicator-link autotest-indicator-link">
                  <Link to={routeMap['accuracy']} className="autotest-indicator-top">
                    <div className="download-indicator-header">
                      {accuracyCompleted.ok ? (
                        <span className="download-indicator-icon success">✓</span>
                      ) : (
                        <span className="download-indicator-icon error">✗</span>
                      )}
                      <span className="download-indicator-title">{accuracyCompleted.title}</span>
                    </div>
                    <div className="download-indicator-url" title={accuracyCompleted.summary}>
                      {accuracyCompleted.summary}
                    </div>
                  </Link>
                  <button
                    type="button"
                    className="autotest-indicator-stop"
                    onClick={dismissAccuracy}
                    aria-label="Dismiss accuracy result"
                    title="Dismiss"
                  >
                    ✕
                  </button>
                </div>
              </div>
            )}
          </div>
        )}
        <button
          className="sidebar-toggle"
          onClick={toggleTheme}
          aria-label={theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode'}
          title={theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode'}
        >
          {theme === 'light' ? (
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
            </svg>
          ) : (
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <circle cx="12" cy="12" r="5" />
              <line x1="12" y1="1" x2="12" y2="3" />
              <line x1="12" y1="21" x2="12" y2="23" />
              <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
              <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
              <line x1="1" y1="12" x2="3" y2="12" />
              <line x1="21" y1="12" x2="23" y2="12" />
              <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
              <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
            </svg>
          )}
        </button>
        <button
          className="sidebar-toggle"
          onClick={toggleSidebar}
          aria-label={sidebarCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
          aria-expanded={!sidebarCollapsed}
          title={sidebarCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
        >
          {sidebarCollapsed ? (
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <polyline points="9 18 15 12 9 6" />
            </svg>
          ) : (
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <polyline points="15 18 9 12 15 6" />
            </svg>
          )}
        </button>
      </aside>
      <main className="main-content">{children}</main>
    </div>
  );
}
