// app-shell.jsx - top-level layout shell for Sports Science OS.
// Owns only layout mounting. State, storage, repositories, and page logic stay in app.jsx.

// ── macOS chrome wrapper (P2-M1) — feature flag, DEFAULT OFF ──────────────────
// Flag: URL param `?macshell=1`. When absent, AppShell renders EXACTLY as before
// (no extra wrapper, no class names, zero DOM/behavior diff). URL param only.
function macShellEnabled() {
  try {
    return new URLSearchParams(window.location.search).has('macshell');
  } catch (e) {
    return false;
  }
}

// macOS sidebar groups. Each item maps ONLY to a real existing view id that the
// legacy NavRail already routes to (via getNavItems). No invented routes, no stubs.
// active-state + labels are read from getNavItems(view) so the MacSidebar shares the
// single canonical nav data source and the same onNav handler as NavRail.
// 分组镜像产品闭环（用户2026-07-05定稿）：人 → 采集 → 审核 → 训练 → 输出。
const MAC_NAV_GROUPS = [
  { id: 'athletes', label: 'Athletes', items: ['individual', 'team'] },
  { id: 'capture', label: 'Capture', items: ['field-test', 'cmj', 'injury'] },
  { id: 'review', label: 'Review', items: ['review-database'] },
  { id: 'training', label: 'Training', items: ['training', 'rehab', 'calendar'] },
  { id: 'output', label: 'Output', items: ['report'] },
  { id: 'knowledge', label: 'Knowledge', items: ['knowledge', 'norms'] },
];

function MacSidebar({ view, lang, onNav, onOpenSettings }) {
  const en = lang === 'en';
  // Same canonical nav model the legacy NavRail consumes (id / labels / on-state).
  const navItems = getNavItems(view);
  const byId = {};
  navItems.forEach(it => { byId[it.id] = it; });
  const settingsActive = view === 'settings';

  // Bilingual tooltip label for an item id (falls back to id).
  const labelFor = (it) => (it ? `${it.zh} · ${it.en}` : '');

  // Flat list of icon buttons (mirrors the grouped model, one icon per item).
  const flatItems = [];
  MAC_NAV_GROUPS.forEach(group => {
    group.items.forEach(id => {
      const it = byId[id];
      if (it) flatItems.push(it);
    });
  });

  return (
    <aside className="mac-sidebar mac-sidebar-icons">
      <div className="mac-icon-list">
        {flatItems.map(it => (
          <button
            key={it.id}
            type="button"
            className={`mac-icon-btn${it.on ? ' active' : ''}`}
            onClick={() => onNav(it.id)}
            aria-current={it.on ? 'page' : undefined}
            aria-label={labelFor(it)}
            title={labelFor(it)}
          >
            <span className="mac-icon-glyph">{navIcon(it.id, 19)}</span>
            <span className="mac-icon-tip">{labelFor(it)}</span>
          </button>
        ))}
      </div>
      <button
        type="button"
        className={`mac-icon-btn mac-icon-settings${settingsActive ? ' active' : ''}`}
        onClick={() => onOpenSettings()}
        aria-current={settingsActive ? 'page' : undefined}
        aria-label={en ? 'Settings · 设置' : '设置 · Settings'}
        title={en ? 'Settings · 设置' : '设置 · Settings'}
      >
        <span className="mac-icon-glyph">{navIcon('settings', 19)}</span>
        <span className="mac-icon-tip">{en ? 'Settings · 设置' : '设置 · Settings'}</span>
      </button>
    </aside>
  );
}

function MacAiAssistant(props) {
  const Assistant = window.AiAssistant;
  return Assistant ? <Assistant {...props} /> : null;
}

// ── Global athlete-context bar (NAV-4) — the single always-on anchor in the
// macshell titlebar. Shows which athlete + season you are working on, on every
// route, and consolidates the season control (removed from per-route TopBar /
// team page). Lightweight: avatar initials + name + #num · position · meta,
// the shared SeasonSelector, and a → individual-view link. Never crashes on a
// null athlete (squad/team overview with none selected → neutral hint + season).
function MacContextBar({ athlete, season, setSeason, seasons, onOpenIndividual, lang }) {
  const en = lang === 'en';
  // SeasonSelector lives in components.jsx (module-scoped const, re-exported on
  // window) — reference it through window, not a bare identifier.
  const Season = window.SeasonSelector;
  // Athlete initials for the avatar (first char of each name token, up to 2).
  const initials = athlete && athlete.name
    ? athlete.name.trim().split(/\s+/).slice(0, 2).map(w => w[0]).join('').toUpperCase()
    : '';
  // Meta line uses only real identity fields (no invented `team`): #jersey,
  // position, sport — each omitted gracefully when absent.
  const metaParts = athlete
    ? [
        athlete.jersey != null && athlete.jersey !== '' ? `#${athlete.jersey}` : null,
        athlete.position || null,
        athlete.sport || null,
      ].filter(Boolean)
    : [];

  return (
    <div className="mac-context-bar" role="group" aria-label={en ? 'Athlete context' : '运动员上下文'}>
      {athlete ? (
        <button
          type="button"
          className="mac-context-athlete"
          onClick={onOpenIndividual}
          title={en ? `Open ${athlete.name}’s individual view` : `打开 ${athlete.name} 的个人页`}
        >
          <span className="mac-context-avatar" aria-hidden="true">{initials}</span>
          <span className="mac-context-idblock">
            <span className="mac-context-name">{athlete.name}</span>
            {metaParts.length > 0 && (
              <span className="mac-context-meta">{metaParts.join(' · ')}</span>
            )}
          </span>
        </button>
      ) : (
        <span className="mac-context-empty">{en ? 'No athlete selected' : '未选择运动员'}</span>
      )}
      <span className="mac-context-divider" aria-hidden="true" />
      {Season && <Season value={season} onChange={setSeason} seasons={seasons} />}
      {athlete && (
        <button
          type="button"
          className="mac-context-link"
          onClick={onOpenIndividual}
          title={en ? 'Go to individual view' : '前往个人页'}
        >
          <span>{en ? 'Individual' : '个人页'}</span>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14m-6-6 6 6-6 6"/></svg>
        </button>
      )}
    </div>
  );
}

const WORKSPACE_NAV_GROUPS = [
  { id: 'workspace', label: '工作区', items: ['team', 'individual', 'cmj', 'training', 'rehab', 'injury'] },
  { id: 'workflow', label: '工作流', items: ['field-test', 'review-database', 'report', 'calendar', 'knowledge', 'norms'] },
];

const WORKSPACE_ROUTE_COPY = {
  team: ['团队工作台', '负荷、测试覆盖与教练讨论'],
  individual: ['运动员', '纵向档案与个体决策'],
  cmj: ['测力台', '采集、分析与历史会话'],
  sj: ['测力台', '采集、分析与历史会话'],
  imtp: ['测力台', '采集、分析与历史会话'],
  training: ['训练', '计划、执行与反馈'],
  injury: ['损伤筛查', '病例登记、筛查事实与评估记录'],
  rehab: ['康复训练', '康复计划、执行、评估与回归记录'],
  'movement-screen': ['动作筛查', '筛查、记录与恢复进程'],
  'field-test': ['测试录入', '现场测试与批量数据导入'],
  'review-database': ['审核数据库', '确认结果、来源与训练连接'],
  report: ['报告编辑', '选择事实、组织叙事、预览并交付'],
  calendar: ['日历', '训练与测试安排'],
  norms: ['常模库', '参考群体、来源证据与指标解释'],
  knowledge: ['知识库', 'Universal / Individual 资料与可追溯检索'],
  settings: ['设置', '数据、指标与工作区偏好'],
};

function WorkspaceSidebar({ view, lang, onNav, onOpenSettings }) {
  const items = getNavItems(view);
  const byId = Object.fromEntries(items.map(item => [item.id, item]));
  const label = (item) => lang === 'en' ? item.en : item.zh;
  return (
    <aside className="workspace-sidebar navrail" aria-label="主导航">
      <button type="button" className="workspace-brand" onClick={() => onNav('team')} aria-label="Axis Performance 团队工作台">
        <span className="workspace-brand-mark">A</span>
        <span><strong>Axis</strong><small>Performance</small></span>
      </button>
      <nav className="workspace-nav">
        {WORKSPACE_NAV_GROUPS.map(group => (
          <section key={group.id} className="workspace-nav-group">
            <div className="workspace-nav-label">{group.label}</div>
            {group.items.map(id => {
              const item = byId[id];
              if (!item) return null;
              return (
                <button key={id} type="button" className={`workspace-nav-link${item.on ? ' on' : ''}`}
                  onClick={() => onNav(id)} aria-current={item.on ? 'page' : undefined}
                  aria-label={label(item)} title={label(item)}>
                  <span>{navIcon(id, 18)}</span><b>{label(item)}</b>
                </button>
              );
            })}
          </section>
        ))}
      </nav>
      <button type="button" className={`workspace-nav-link workspace-settings${view === 'settings' ? ' on' : ''}`}
        onClick={() => onOpenSettings()} aria-current={view === 'settings' ? 'page' : undefined} aria-label={lang === 'en' ? 'Settings · 设置' : '设置 · Settings'}>
        <span>{navIcon('settings', 18)}</span><b>{lang === 'en' ? 'Settings' : '设置'}</b>
      </button>
    </aside>
  );
}

function WorkspaceTopbar({ view, athletes, onSelectAthlete, onMenu, showMenu, lang, aiReadModel, aiDecisionTools, onNav, onOpenSettings, showAi = true }) {
  const [query, setQuery] = useState('');
  const copy = WORKSPACE_ROUTE_COPY[view] || [view, 'Performance workspace'];
  const matches = query.trim()
    ? (athletes || []).filter(a => String(a.name || '').toLowerCase().includes(query.trim().toLowerCase())).slice(0, 6)
    : [];
  return (
    <header className="workspace-topbar app-top-nav">
      <div className="workspace-title">
        <button type="button" className="app-menu-btn" onClick={onMenu} aria-label="打开运动员列表">☰</button>
        <div><h1>{copy[0]}</h1><p>{copy[1]}</p></div>
      </div>
      <div className="workspace-top-actions">
        <div className="workspace-search">
          <span aria-hidden="true">⌕</span>
          <input value={query} onChange={e => setQuery(e.target.value)} aria-label="搜索运动员" />
          {!query && <span className="workspace-search-hint" aria-hidden="true">搜索运动员</span>}
          {matches.length > 0 && (
            <div className="workspace-search-results">
              {matches.map(a => (
                <button key={a.id} type="button" onClick={() => { onSelectAthlete(a.id); setQuery(''); }}>
                  <span>{String(a.name || '?').slice(0, 1)}</span><b>{a.name}</b><small>{a.position || a.sport || '运动员'}</small>
                </button>
              ))}
            </div>
          )}
        </div>
        {showAi && window.AiAssistant && <window.AiAssistant readModel={aiReadModel} decisionTools={aiDecisionTools} view={view} onNavigate={onNav} onSelectAthlete={onSelectAthlete} />}
        <button type="button" className="workspace-bell" aria-label="通知">♢</button>
        {(() => {
          const auth = window.AxisAuthRuntime && window.AxisAuthRuntime.getContext ? window.AxisAuthRuntime.getContext() : null;
          const name = auth?.displayName || auth?.email || 'Axis user';
          const role = auth?.role || 'viewer';
          return <button type="button" className="workspace-user" onClick={() => onOpenSettings('account')} aria-label="打开账号与权限设置">
            <span>{String(name).trim().slice(0, 2).toUpperCase()}</span><div><b>{name}</b><small>{role}</small></div>
          </button>;
        })()}
      </div>
    </header>
  );
}

function AppShell({
  isHoriz,
  sidebarOpen,
  rosterVisible,
  rosterCollapsed,
  view,
  lang,
  onNav,
  onOpenSettings,
  onMenu,
  onCloseSidebar,
  compareMode,
  compareIds,
  athletes,
  onClearCompare,
  onExitCompare,
  onJumpCompare,
  athlete,
  season,
  setSeason,
  seasons,
  onOpenIndividual,
  onSelectAthlete,
  aiReadModel,
  aiDecisionTools,
  sidebar,
  children,
}) {
  const useMacShell = macShellEnabled();
  const legacyTree = (
    <div
      className={`app has-navrail route-${view} ${isHoriz ? '' : 'layout-vertical'} ${sidebarOpen ? 'sidebar-open' : ''} ${rosterVisible ? (rosterCollapsed ? 'roster-collapsed' : '') : 'no-roster'}`}
      style={{ background: 'var(--bg)' }}
    >
      <WorkspaceSidebar view={view} lang={lang} onNav={onNav} onOpenSettings={onOpenSettings} />
      <BottomNav view={view} lang={lang} onNav={onNav} />
      <WorkspaceTopbar view={view} athletes={athletes} onSelectAthlete={onSelectAthlete}
        showMenu={rosterVisible} onMenu={onMenu} lang={lang} aiReadModel={aiReadModel} aiDecisionTools={aiDecisionTools} onNav={onNav}
        onOpenSettings={onOpenSettings}
        showAi={!useMacShell} />
      <CompareStatusBar
        compareMode={compareMode}
        compareIds={compareIds}
        athletes={athletes}
        onClear={onClearCompare}
        onExit={onExitCompare}
        onJump={onJumpCompare}
      />
      <div className="app-sidebar-backdrop" onClick={onCloseSidebar} />
      {sidebar}
      {children}
    </div>
  );

  // DEFAULT OFF: identical legacy render path, no wrapper, no added class names.
  if (!useMacShell) return legacyTree;

  // Flag ON: compose new chrome AROUND the untouched legacy tree (Shell must
  // compose, not replace). NavRail + AppTopNav are hidden via the `.mac-shell`
  // CSS scope (Single Chrome Rule); the legacy tree and its children render as-is.
  return (
    <div className="mac-shell">
      <div className="mac-window">
        <div className="mac-titlebar">
          <div className="mac-traffic" aria-hidden="true">
            <span className="mac-dot mac-dot-red" />
            <span className="mac-dot mac-dot-yellow" />
            <span className="mac-dot mac-dot-green" />
          </div>
          <div className="mac-app-title">Performance Dashboard</div>
          <MacContextBar
            athlete={athlete}
            season={season}
            setSeason={setSeason}
            seasons={seasons}
            onOpenIndividual={onOpenIndividual}
            lang={lang}
          />
        </div>
        <div className="mac-body">
          <MacSidebar view={view} lang={lang} onNav={onNav} onOpenSettings={onOpenSettings} />
          <div className="mac-content">
            {legacyTree}
          </div>
        </div>
        <MacAiAssistant readModel={aiReadModel} decisionTools={aiDecisionTools} view={view} onNavigate={onNav} onSelectAthlete={onSelectAthlete} />
      </div>
    </div>
  );
}
