// Issues list, Kanban, Teams, Engines, Skills, Agent Execution panel

// ---- Issues List ----
const ISSUE_VIEWS = [
  { id: 'all',       label: 'All' },
  { id: 'mine',      label: 'My queue' },
  { id: 'running',   label: 'Running', pulse: true },
  { id: 'approval',  label: 'Needs approval' },
  { id: 'blocked',   label: 'Blocked' },
  { id: 'archived',  label: 'Archived' },
];

const matchesView = (iss, view) => {
  if (view === 'archived') return iss.status === 'archived';
  if (iss.status === 'archived') return false;
  switch (view) {
    case 'all':      return true;
    case 'mine':     return iss.assignee?.kind === 'human' && iss.assignee?.name === 'Xie Ming';
    case 'running':  return iss.status === 'working';
    case 'approval': return iss.status === 'review';
    case 'blocked':  return iss.priority === 'urgent' && iss.status === 'todo';
    default:         return true;
  }
};

const IssuesList = ({ onNav, scopeProject, embedded }) => {
  const [view, setView] = useState('all');
  const [projFilter, setProjFilter] = useState(scopeProject || 'all'); // 'all' | project id
  const [query, setQuery] = useState('');
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [groupBy, setGroupBy] = useState('status');
  const [sortBy, setSortBy] = useState('updated');
  const projById = Object.fromEntries(PROJECTS.map(p => [p.id, p]));
  const settingsRef = React.useRef(null);

  // Keep in sync if scope changes
  React.useEffect(() => { if (scopeProject) setProjFilter(scopeProject); }, [scopeProject]);

  // Close settings popover on outside click / Escape
  React.useEffect(() => {
    if (!settingsOpen) return;
    const onDoc = (e) => {
      if (settingsRef.current && !settingsRef.current.contains(e.target)) setSettingsOpen(false);
    };
    const onKey = (e) => { if (e.key === 'Escape') setSettingsOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
    };
  }, [settingsOpen]);

  // Active filter count: anything non-default counts
  const activeFilterCount =
    (view !== 'all' ? 1 : 0) +
    (!scopeProject && projFilter !== 'all' ? 1 : 0) +
    (groupBy !== 'status' ? 1 : 0) +
    (sortBy !== 'updated' ? 1 : 0);

  const resetAll = () => {
    setView('all');
    if (!scopeProject) setProjFilter('all');
    setQuery('');
    setGroupBy('status');
    setSortBy('updated');
  };

  // Counts
  const viewCount = (v) => {
    const scope = scopeProject ? ISSUES.filter(i => i.proj === scopeProject) : ISSUES;
    return scope.filter(i => matchesView(i, v)).length;
  };
  const projCount = (pid) => {
    const scope = view === 'archived'
      ? ISSUES.filter(i => i.status === 'archived')
      : ISSUES.filter(i => i.status !== 'archived');
    return pid === 'all' ? scope.length : scope.filter(i => i.proj === pid).length;
  };

  // Final filtered list: view ∩ project ∩ query
  const filtered = ISSUES.filter(i => {
    if (scopeProject && i.proj !== scopeProject) return false;
    if (!matchesView(i, view)) return false;
    if (!scopeProject && projFilter !== 'all' && i.proj !== projFilter) return false;
    if (query) {
      const q = query.toLowerCase();
      if (!i.title.toLowerCase().includes(q) &&
          !i.id.toLowerCase().includes(q) &&
          !i.labels.some(l => l.toLowerCase().includes(q))) return false;
    }
    return true;
  });

  const scopeIssues = scopeProject ? ISSUES.filter(i => i.proj === scopeProject) : ISSUES;
  const runningNow = scopeIssues.filter(i => i.status === 'working').length;
  const archivedCount = scopeIssues.filter(i => i.status === 'archived').length;

  const activeView = ISSUE_VIEWS.find(v => v.id === view) || ISSUE_VIEWS[0];
  const activeProj = projFilter === 'all' ? null : PROJECTS.find(p => p.id === projFilter);

  return (
    <div className="fade-in" style={{display:'flex',flexDirection:'column',height:'100%'}}>
      {!embedded && (
        <div className="page-head">
          <div>
            <div className="page-title">Issues</div>
            <div className="page-sub">
              {ISSUES.length - archivedCount} open · {runningNow} running now · {archivedCount} archived
            </div>
          </div>
          <div className="page-actions">
            <button className="ibtn primary"><Icon name="plus" size={13} /> New issue</button>
          </div>
        </div>
      )}

      {/* Slim toolbar: search + active filter pills + settings */}
      {(() => {
        const toolbarEl = (
        <div className={`issues-toolbar ${embedded ? 'issues-toolbar--embedded' : ''}`}>
        <div className="search">
          <Icon name="search" size={13} className="dim" />
          <input
            placeholder="Search title, id, label…"
            value={query}
            onChange={e => setQuery(e.target.value)}
          />
          <kbd style={{fontSize:10,color:'var(--fg-4)',fontFamily:'Geist Mono'}}>⌘F</kbd>
        </div>

        {/* Active filter pills — only render when filter is non-default */}
        {view !== 'all' && (
          <button className="filter-pill" onClick={() => setView('all')} title="Clear view filter">
            {activeView.pulse && <span className="view-tab-pulse" />}
            <span className="filter-pill-label">View</span>
            <span className="filter-pill-val">{activeView.label}</span>
            <Icon name="close" size={10} />
          </button>
        )}
        {!scopeProject && projFilter !== 'all' && activeProj && (
          <button className="filter-pill" onClick={() => setProjFilter('all')} title="Clear project filter">
            <span className="proj-chip-swatch" style={{background: activeProj.color, width:8, height:8}} />
            <span className="filter-pill-label">Project</span>
            <span className="filter-pill-val">{activeProj.name}</span>
            <Icon name="close" size={10} />
          </button>
        )}
        {groupBy !== 'status' && (
          <button className="filter-pill" onClick={() => setGroupBy('status')}>
            <span className="filter-pill-label">Group</span>
            <span className="filter-pill-val" style={{textTransform:'capitalize'}}>{groupBy}</span>
            <Icon name="close" size={10} />
          </button>
        )}
        {sortBy !== 'updated' && (
          <button className="filter-pill" onClick={() => setSortBy('updated')}>
            <span className="filter-pill-label">Sort</span>
            <span className="filter-pill-val" style={{textTransform:'capitalize'}}>{sortBy}</span>
            <Icon name="close" size={10} />
          </button>
        )}

        <span className="spacer" />

        <div className="issues-settings-wrap" ref={settingsRef}>
          <button
            className={`ibtn bordered issues-settings-btn ${settingsOpen?'on':''}`}
            onClick={() => setSettingsOpen(o => !o)}
            aria-expanded={settingsOpen}
            title="View options"
          >
            <Icon name="settings" size={13} />
            <span>Options</span>
            {activeFilterCount > 0 && <span className="issues-settings-badge">{activeFilterCount}</span>}
          </button>

          {settingsOpen && (
            <div className="issues-settings-pop" role="dialog" aria-label="View options">
              <div className="issues-settings-head">
                <span>View options</span>
                {activeFilterCount > 0 && (
                  <button className="ibtn ghost" onClick={resetAll} style={{fontSize:11}}>Reset</button>
                )}
              </div>

              {/* Saved views */}
              <div className="issues-settings-section">
                <div className="issues-settings-label">Saved view</div>
                <div className="issues-settings-views">
                  {ISSUE_VIEWS.map(v => {
                    const n = viewCount(v.id);
                    const active = view === v.id;
                    return (
                      <button
                        key={v.id}
                        className={`issues-settings-view ${active?'on':''}`}
                        onClick={() => setView(v.id)}
                      >
                        {v.pulse && n > 0 && <span className="view-tab-pulse" />}
                        <span>{v.label}</span>
                        <span className="view-tab-count">{n}</span>
                        {active && <Icon name="check" size={11} className="issues-settings-check" />}
                      </button>
                    );
                  })}
                </div>
              </div>

              {/* Project filter — hidden when scoped */}
              {!scopeProject && (
                <div className="issues-settings-section">
                  <div className="issues-settings-label">Project</div>
                  <div className="issues-settings-projs">
                    <button
                      className={`issues-settings-proj ${projFilter==='all'?'on':''}`}
                      onClick={() => setProjFilter('all')}
                    >
                      <span className="proj-chip-swatch" style={{background:'linear-gradient(135deg,#3D63FF 0%,#8B5CF6 50%,#00A870 100%)'}} />
                      <span>All projects</span>
                      <span className="proj-chip-count">{projCount('all')}</span>
                    </button>
                    {PROJECTS.map(p => {
                      const n = projCount(p.id);
                      return (
                        <button
                          key={p.id}
                          className={`issues-settings-proj ${projFilter===p.id?'on':''}`}
                          onClick={() => setProjFilter(p.id)}
                          title={p.desc}
                        >
                          <span className="proj-chip-swatch" style={{background:p.color}} />
                          <span>{p.name}</span>
                          <span className="proj-chip-count">{n}</span>
                        </button>
                      );
                    })}
                  </div>
                </div>
              )}

              {/* Field filters */}
              <div className="issues-settings-section">
                <div className="issues-settings-label">Filter by field</div>
                <div className="issues-settings-fields">
                  <button className="issues-settings-field"><Icon name="plus" size={11} /> Priority</button>
                  <button className="issues-settings-field"><Icon name="plus" size={11} /> Assignee</button>
                  <button className="issues-settings-field"><Icon name="plus" size={11} /> Label</button>
                  <button className="issues-settings-field"><Icon name="plus" size={11} /> Node</button>
                </div>
              </div>

              {/* Group + Sort */}
              <div className="issues-settings-section issues-settings-row2">
                <div>
                  <div className="issues-settings-label">Group by</div>
                  <select className="issues-settings-select" value={groupBy} onChange={e => setGroupBy(e.target.value)}>
                    <option value="status">Status</option>
                    <option value="priority">Priority</option>
                    <option value="assignee">Assignee</option>
                    <option value="project">Project</option>
                    <option value="none">None</option>
                  </select>
                </div>
                <div>
                  <div className="issues-settings-label">Sort by</div>
                  <select className="issues-settings-select" value={sortBy} onChange={e => setSortBy(e.target.value)}>
                    <option value="updated">Updated</option>
                    <option value="created">Created</option>
                    <option value="priority">Priority</option>
                    <option value="title">Title</option>
                  </select>
                </div>
              </div>
            </div>
          )}
        </div>
      </div>
        );
        if (embedded && typeof document !== 'undefined') {
          const slot = document.getElementById('pd-toolbar-issues-slot');
          if (slot) return ReactDOM.createPortal(toolbarEl, slot);
        }
        return toolbarEl;
      })()}

      <div style={{flex:1,overflow:'auto',padding:'0 16px'}}>
        <table className="table issues-table">
          <thead>
            <tr>
              <th style={{width:30}}></th>
              <th style={{width:80}}>ID</th>
              <th>Title</th>
              <th style={{width:110}}>Status</th>
              <th style={{width:90}}>Priority</th>
              <th style={{width:180}}>Assignee</th>
              {!scopeProject && <th style={{width:110}}>Project</th>}
              <th style={{width:70,textAlign:'right'}}>Upd.</th>
            </tr>
          </thead>
          <tbody>
            {filtered.length === 0 && (
              <tr><td colSpan={scopeProject ? 7 : 8} style={{textAlign:'center',padding:'48px 16px',color:'var(--fg-3)',fontSize:12.5}}>
                No issues match. <button className="ibtn" style={{marginLeft:6}} onClick={() => { setView('all'); if (!scopeProject) setProjFilter('all'); setQuery(''); }}>Clear filters</button>
              </td></tr>
            )}
            {filtered.map(iss => {
              const p = projById[iss.proj];
              const isArchived = iss.status === 'archived';
              return (
                <tr key={iss.id}
                  className={isArchived ? 'archived-row' : ''}
                  onClick={() => { window.__currentIssue = iss.id; onNav('issue-detail'); }}>
                  <td><span className={`priority-icon priority-${iss.priority==='urgent'?'high':iss.priority}`}><i /><i /><i /></span></td>
                  <td className="mono dim" style={{fontSize:11.5}}>{iss.id}</td>
                  <td>
                    <div style={{display:'flex',alignItems:'center',gap:8}}>
                      <span style={{fontWeight:450,textDecoration: isArchived ? 'line-through' : 'none', textDecorationColor: 'var(--fg-4)'}}>{iss.title}</span>
                      {iss.labels.map(l => (
                        <span key={l} className="chip" style={{fontSize:10.5,padding:'1px 6px'}}>{l}</span>
                      ))}
                      {iss.status === 'working' && iss.eta && (
                        <span className="chip chip-working" style={{fontSize:10.5,padding:'1px 6px',marginLeft:4}}>
                          <span className="running-pulse" style={{width:5,height:5}}/> ETA {iss.eta}
                        </span>
                      )}
                      {isArchived && iss.archivedReason && (
                        <span className="chip" style={{fontSize:10.5,padding:'1px 6px',marginLeft:4,color:'var(--fg-3)'}}>
                          {iss.archivedReason}
                        </span>
                      )}
                    </div>
                  </td>
                  <td><span className={`chip chip-${iss.status} dot`}>{iss.status[0].toUpperCase()+iss.status.slice(1)}</span></td>
                  <td style={{color:iss.priority==='urgent'?'var(--status-blocked)':iss.priority==='high'?'var(--status-working)':'var(--fg-2)',fontSize:11.5}}>
                    {iss.priority[0].toUpperCase()+iss.priority.slice(1)}
                  </td>
                  <td><AssigneeChip a={iss.assignee} size="sm" /></td>
                  {!scopeProject && (
                    <td>
                      <span style={{display:'inline-flex',alignItems:'center',gap:6}}>
                        <span style={{width:8,height:8,borderRadius:2,background:p.color}} />
                        <span style={{fontSize:11.5}}>{p.name}</span>
                      </span>
                    </td>
                  )}
                  <td className="mono dim" style={{fontSize:11,textAlign:'right'}}>{iss.updated}</td>
                </tr>
              );
            })}
          </tbody>
        </table>

        {/* Mobile card list — visibility flipped by CSS */}
        <div className="issues-mobile">
          {filtered.length === 0 && (
            <div style={{textAlign:'center',padding:'40px 16px',color:'var(--fg-3)',fontSize:13}}>
              No issues match.
              <button className="ibtn" style={{marginLeft:8}} onClick={() => { setView('all'); setProjFilter('all'); setQuery(''); }}>Clear</button>
            </div>
          )}
          {filtered.map(iss => {
            const p = projById[iss.proj];
            const isArchived = iss.status === 'archived';
            return (
              <div key={iss.id} className="issue-mcard"
                onClick={() => { window.__currentIssue = iss.id; onNav('issue-detail'); }}>
                <div className="issue-mcard-top">
                  <span className={`priority-icon priority-${iss.priority==='urgent'?'high':iss.priority}`}><i /><i /><i /></span>
                  <span>{iss.id}</span>
                  <span className={`chip chip-${iss.status} dot`} style={{fontSize:10}}>{iss.status[0].toUpperCase()+iss.status.slice(1)}</span>
                  <span style={{marginLeft:'auto',fontSize:11}}>{iss.updated}</span>
                </div>
                <div className="issue-mcard-title" style={{textDecoration: isArchived ? 'line-through' : 'none', textDecorationColor: 'var(--fg-4)'}}>{iss.title}</div>
                <div className="issue-mcard-meta">
                  {!scopeProject && (
                    <span style={{display:'inline-flex',alignItems:'center',gap:5}}>
                      <span style={{width:7,height:7,borderRadius:2,background:p.color}} />
                      {p.name}
                    </span>
                  )}
                  {iss.labels.slice(0,2).map(l => (
                    <span key={l} className="chip" style={{fontSize:10,padding:'1px 6px'}}>{l}</span>
                  ))}
                  <span style={{marginLeft:'auto'}}><AssigneeChip a={iss.assignee} size="sm" /></span>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
};

// ---- Kanban board ----
const Kanban = ({ scopeProject, embedded }) => {
  const initial = scopeProject ? ISSUES.filter(i => i.proj === scopeProject) : ISSUES;
  const [items, setItems] = useState(initial);
  const [dragId, setDragId] = useState(null);
  const [overCol, setOverCol] = useState(null);

  React.useEffect(() => {
    setItems(scopeProject ? ISSUES.filter(i => i.proj === scopeProject) : ISSUES);
  }, [scopeProject]);

  const cols = [
    { key: 'todo',    title: 'Todo',    color: 'var(--status-todo)' },
    { key: 'working', title: 'Working', color: 'var(--status-working)' },
    { key: 'review',  title: 'Review',  color: 'var(--status-review)' },
    { key: 'done',    title: 'Done',    color: 'var(--status-done)' },
  ];

  const onDrop = (colKey) => {
    setItems(prev => prev.map(i => i.id === dragId ? {...i, status: colKey} : i));
    setDragId(null); setOverCol(null);
  };

  return (
    <div className="fade-in" style={{display:'flex',flexDirection:'column',height:'100%'}}>
      {!embedded && (
        <div className="page-head">
          <div>
            <div className="page-title">Board</div>
            <div className="page-sub">Drag cards across columns · {items.length} issues</div>
          </div>
          <div className="page-actions">
            <button className="ibtn bordered"><Icon name="filter" size={13} /> All projects</button>
            <button className="ibtn bordered"><Icon name="layers" size={13} /> Group: Project</button>
          </div>
        </div>
      )}
      <div className="kanban">
        {cols.map(col => {
          const colItems = items.filter(i => i.status === col.key);
          return (
            <div key={col.key}
              className="kcol"
              onDragOver={e => { e.preventDefault(); setOverCol(col.key); }}
              onDragLeave={() => setOverCol(c => c === col.key ? null : c)}
              onDrop={() => onDrop(col.key)}
              style={overCol === col.key ? {borderColor: 'var(--accent)', borderStyle: 'dashed'} : {}}
            >
              <div className="kcol-head">
                <div className="kcol-title">
                  <span style={{width:8,height:8,borderRadius:50,background:col.color}} />
                  {col.title}
                  <span className="kcol-count">{colItems.length}</span>
                </div>
                <Icon name="plus" size={13} className="dim" style={{cursor:'pointer'}} />
                <Icon name="dots" size={14} className="dim" style={{cursor:'pointer'}} />
              </div>
              <div className="kcol-body">
                {colItems.map(iss => (
                  <div key={iss.id} className={`kcard ${dragId===iss.id?'dragging':''}`}
                    draggable onDragStart={() => setDragId(iss.id)} onDragEnd={() => { setDragId(null); setOverCol(null); }}>
                    <div style={{display:'flex',alignItems:'center',gap:6,marginBottom:2}}>
                      <span className="kcard-id">{iss.id}</span>
                      <span className={`priority-icon priority-${iss.priority==='urgent'?'high':iss.priority}`}><i /><i /><i /></span>
                      <span className="spacer" />
                      {iss.labels.slice(0,1).map(l => <span key={l} className="chip" style={{fontSize:10,padding:'1px 5px'}}>{l}</span>)}
                    </div>
                    <div className="kcard-title">{iss.title}</div>
                    <div className="kcard-meta">
                      <AssigneeChip a={iss.assignee} size="sm" />
                      <span className="right">
                        {iss.status === 'working' && iss.eta && (
                          <span className="mono" style={{color:'var(--status-working)',fontSize:10.5,display:'inline-flex',alignItems:'center',gap:4}}>
                            <span className="running-pulse" style={{width:5,height:5}}/> {iss.eta}
                          </span>
                        )}
                        <span className="dim mono" style={{fontSize:10.5,marginLeft:6}}>{iss.updated}</span>
                      </span>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};

// ---- Teams ----
const Teams = () => {
  const people = AGENTS_BY_ENGINE.human;
  const activeCount = people.filter(p => p.active).length;
  const projectCount = new Set(people.flatMap(p => p.projects)).size;
  const roleGroups = {};
  people.forEach(p => { (roleGroups[p.role] = roleGroups[p.role] || []).push(p); });

  return (
    <div className="fade-in">
      <div className="page-head">
        <div>
          <div className="page-title">Teams</div>
          <div className="page-sub">{people.length} members · {activeCount} online · across {projectCount} projects</div>
        </div>
        <div className="page-actions">
          <button className="ibtn bordered"><Icon name="filter" size={13} /> Filter</button>
          <button className="ibtn primary"><Icon name="plus" size={13} /> Invite member</button>
        </div>
      </div>

      <div className="team-roster">
        <div className="team-row team-row-head">
          <div>Member</div>
          <div>Role</div>
          <div>Projects</div>
          <div>Status</div>
          <div>Timezone</div>
          <div style={{textAlign:'right'}}>Shipped · 30d</div>
        </div>
        {people.map((p,i) => (
          <div key={i} className={`team-row ${!p.active?'dim-row':''}`}>
            <div className="team-member">
              <span className="avatar" style={{width:32,height:32,fontSize:12,background:p.color,borderWidth:0}}>{p.avatar}</span>
              <div style={{minWidth:0}}>
                <div className="team-name">
                  {p.name}
                  {p.active && <span className="presence-dot" />}
                </div>
                <div className="team-handle mono">{p.handle}</div>
              </div>
            </div>
            <div>
              <div className="team-role">{p.role}</div>
              <div className="team-title">{p.title}</div>
            </div>
            <div className="team-projs">
              {p.projects.slice(0,3).map(pid => {
                const pr = PROJECTS.find(x => x.id === pid);
                return pr ? (
                  <span key={pid} className="team-proj-chip" style={{background: pr.color}} title={pr.name}>{pid.slice(0,2)}</span>
                ) : null;
              })}
              {p.projects.length > 3 && <span className="team-proj-chip more">+{p.projects.length-3}</span>}
            </div>
            <div>
              <div className={`team-status ${p.active?'on':'off'}`}>
                {p.status}
              </div>
              <div className="team-last-seen">{p.lastSeen}</div>
            </div>
            <div className="team-tz mono">{p.tz}</div>
            <div className="team-shipped">
              <div className="team-ship-num mono">{p.shipped}</div>
              <div className="team-ship-sub">
                <span className="mono">{p.reviews}</span> reviews · <span className="mono">{p.runs}</span> runs
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

const EngineGroup = ({ title, desc, logoClass, letter, members, isHuman }) => (
  <div className="engine-group">
    <div className="engine-group-head">
      <div className={`engine-logo ${logoClass}`}>
        {isHuman
          ? <Icon name="team" size={18} />
          : <span style={{fontFamily:'Geist Mono',fontSize:15,fontWeight:700}}>{letter}</span>}
      </div>
      <div style={{flex:1}}>
        <div className="engine-title">{title}</div>
        <div className="engine-desc">{desc}</div>
      </div>
      {!isHuman && <span className="chip chip-done dot">Healthy</span>}
      <Icon name="settings" size={14} className="dim" style={{marginLeft:8,cursor:'pointer'}} />
    </div>
    <div>
      {members.map((m, i) => (
        <div key={i} className="agent-row">
          <div className={`agent-avatar-lg`} style={isHuman ? { background: m.color } : undefined}>
            {isHuman ? m.avatar : <span style={{fontFamily:'Geist Mono',fontSize:13}}>{letter}</span>}
          </div>
          <div style={isHuman ? {} : {}}>
            <div className="agent-name">{m.name}</div>
            <div className="agent-handle">{m.handle}{isHuman ? '' : ` · ${title.toLowerCase()}`}</div>
          </div>
          <div style={{display:'flex',alignItems:'center',gap:6}}>
            <span className={`chip dot ${m.active ? 'chip-done' : ''}`} style={!m.active?{color:'var(--fg-4)',background:'var(--bg-2)'}:{}}>{m.active?'Active':'Idle'}</span>
          </div>
          <div>
            <div className="agent-stat-num">{m.runs}</div>
            <div className="agent-stat-lbl">runs · 7d</div>
          </div>
          <div>
            <div className="agent-stat-num">{m.success}%</div>
            <div className="agent-stat-lbl">success</div>
          </div>
          <div style={{textAlign:'right'}}>
            <div className="agent-stat-num" style={{fontSize:13}}>{m.avgMin}m</div>
            <div className="agent-stat-lbl">avg</div>
          </div>
        </div>
      ))}
    </div>
  </div>
);

// ---- Nodes page ----
const ENGINE_COLORS = { claude: '#D97757', codex: '#10A37F', gemini: '#4285F4' };
const ENGINE_LABEL  = { claude: 'Claude Code', codex: 'Codex', gemini: 'Gemini CLI' };

const Nodes = ({ onNav }) => (
  <div className="fade-in">
    <div className="page-head">
      <div>
        <div className="page-title">Nodes</div>
        <div className="page-sub">Execution hosts · each node runs one or more engines · projects bind to a node</div>
      </div>
      <div className="page-actions">
        <button className="ibtn bordered"><Icon name="refresh" size={13} /> Re-scan</button>
        <button className="ibtn primary"><Icon name="plus" size={13} /> Add node</button>
      </div>
    </div>

    <div className="node-kpi-strip">
      <div className="node-kpi"><div className="kpi-label">Nodes online</div><div className="kpi-val mono">{NODES.filter(n=>n.status==='online').length}<span className="kpi-sub">/ {NODES.length}</span></div></div>
      <div className="node-kpi"><div className="kpi-label">Engines total</div><div className="kpi-val mono">{NODES.reduce((s,n)=>s+n.engines.length,0)}</div></div>
      <div className="node-kpi"><div className="kpi-label">Runs · 7d</div><div className="kpi-val mono">{NODES.reduce((s,n)=>s+n.runs7d,0).toLocaleString()}</div></div>
      <div className="node-kpi"><div className="kpi-label">Degraded</div><div className="kpi-val mono" style={{color:'var(--status-working)'}}>{NODES.reduce((s,n)=>s+n.engines.filter(e=>e.status!=='ready').length,0)}</div></div>
    </div>

    <div className="nodes-grid">
      {NODES.map(n => <NodeCard key={n.id} n={n} onOpen={() => { window.__currentNode = n.id; onNav && onNav('node-detail'); }} />)}
    </div>
  </div>
);

const NodeCard = ({ n, onOpen }) => {
  const degraded = n.engines.some(e => e.status !== 'ready');
  const dotColor = n.status !== 'online' ? 'var(--status-blocked)' : degraded ? 'var(--status-working)' : 'var(--status-done)';
  const isLocal = n.kind === 'local';
  return (
    <div className="node-card" onClick={onOpen}>
      <div className="node-card-head">
        <div className={`node-badge ${isLocal ? 'local' : 'cloud'}`}>
          <Icon name={isLocal ? 'terminal' : 'cloud'} size={16} />
        </div>
        <div style={{flex:1,minWidth:0}}>
          <div style={{display:'flex',alignItems:'center',gap:8}}>
            <div className="node-title mono">{n.name}</div>
            <span className="node-dot" style={{background: dotColor}} />
          </div>
          <div className="node-meta">{n.region} · up {n.uptime}</div>
        </div>
        <Icon name="dots" size={14} className="dim" />
      </div>

      <div className="node-specs">
        <div className="spec-row"><Icon name="cpu" size={11} className="dim" /><span className="dim">{n.specs.cpu}</span></div>
        <div className="spec-row"><Icon name="ram" size={11} className="dim" /><span className="dim">{n.specs.ram} RAM · {n.specs.disk}</span></div>
      </div>

      <div className="node-load">
        {['cpu','ram','disk'].map(k => {
          const v = n.load[k];
          const color = v > 75 ? 'var(--status-blocked)' : v > 50 ? 'var(--status-working)' : 'var(--status-done)';
          return (
            <div key={k} className="load-row">
              <span className="load-label">{k.toUpperCase()}</span>
              <div className="load-track"><div className="load-fill" style={{width: v+'%', background: color}} /></div>
              <span className="load-val mono">{v}%</span>
            </div>
          );
        })}
      </div>

      <div className="node-engines">
        <div className="ne-label">Engines</div>
        {n.engines.map(e => (
          <div key={e.id} className="ne-row">
            <span className="ne-logo" style={{background: ENGINE_COLORS[e.id]}}>{e.id[0].toUpperCase()}</span>
            <span className="ne-name">{ENGINE_LABEL[e.id]}</span>
            <span className="ne-ver mono dim">v{e.version}</span>
            <span className={`ne-status ${e.status}`}>{e.status === 'ready' ? 'ready' : 'degraded'}</span>
            <span className="ne-lat mono">{e.latency}<span className="dim">ms</span></span>
          </div>
        ))}
      </div>

      <div className="node-foot">
        <div className="nf-projects">
          <span className="dim" style={{fontSize:11}}>Projects</span>
          {n.projects.map(pid => {
            const p = PROJECTS.find(x=>x.id===pid);
            return p ? (
              <span key={pid} className="proj-chip">
                <span className="pc-dot" style={{background: p.color}} />
                {p.id}
              </span>
            ) : null;
          })}
        </div>
        <span className="dim mono" style={{fontSize:11}}>{n.runs7d.toLocaleString()} runs · 7d</span>
      </div>
    </div>
  );
};

// ---- Skills page ----
const Skills = () => (
  <div className="fade-in">
    <div className="page-head">
      <div>
        <div className="page-title">Skills</div>
        <div className="page-sub">Reusable prompts + tool bundles · applied by agents across issues</div>
      </div>
      <div className="page-actions">
        <button className="ibtn bordered"><Icon name="upload" size={13} /> Import</button>
        <button className="ibtn primary"><Icon name="plus" size={13} /> New skill</button>
      </div>
    </div>
    <div className="filter-bar">
      <div className="search">
        <Icon name="search" size={13} className="dim" />
        <input placeholder="Search skills…" />
      </div>
      <button className="ibtn bordered" style={{color:'var(--fg)'}}>All <span className="mono" style={{fontSize:10.5,opacity:0.6}}>23</span></button>
      <button className="ibtn">Ship <span className="mono" style={{fontSize:10.5,opacity:0.6}}>8</span></button>
      <button className="ibtn">Review <span className="mono" style={{fontSize:10.5,opacity:0.6}}>6</span></button>
      <button className="ibtn">Ops <span className="mono" style={{fontSize:10.5,opacity:0.6}}>9</span></button>
    </div>
    <div className="skill-grid">
      {SKILLS.map(s => <SkillCard key={s.id} s={s} />)}
    </div>
  </div>
);

const SkillCard = ({ s }) => (
  <div className="skill-card">
    <div className="skill-head">
      <div className="skill-icon"><Icon name={s.icon} size={16} /></div>
      <div>
        <div className="skill-name">{s.name}</div>
        <div className="mono" style={{fontSize:10.5,color:'var(--fg-3)'}}>skill/{s.id}</div>
      </div>
      <span className="spacer"/>
      <Icon name="dots" size={14} className="dim" />
    </div>
    <div className="skill-desc">{s.desc}</div>
    <div className="skill-foot">
      <Icon name="play" size={11} />
      {s.runs.toLocaleString()} runs
      <span className="spacer"/>
      {s.engines.map(eng => (
        <span key={eng} title={eng} style={{
          width:16,height:16,borderRadius:4,display:'grid',placeItems:'center',fontSize:9,color:'white',fontWeight:600,
          background: eng==='claude'?'var(--engine-claude)':eng==='codex'?'var(--engine-codex)':'var(--engine-gemini)',
        }}>{eng[0].toUpperCase()}</span>
      ))}
    </div>
  </div>
);

Object.assign(window, { IssuesList, Kanban, Teams, EngineGroup, Nodes, NodeCard, NodeDetail, Skills, SkillCard });

// ---- Node detail ----
const NODE_TABS = [
  { id: 'overview', label: 'Overview', icon: 'dashboard' },
  { id: 'terminal', label: 'Terminal', icon: 'terminal' },
  { id: 'files',    label: 'Files',    icon: 'folder' },
];

// Fake filesystem per node — project dirs, agent workspaces, logs
function buildFS(n) {
  const projects = PROJECTS.filter(p => n.projects.includes(p.id));
  return {
    '~': {
      kind: 'dir',
      children: {
        'projects': {
          kind: 'dir',
          children: Object.fromEntries(projects.map(p => [p.slug, {
            kind: 'dir', owner: p.id,
            children: {
              'src':       { kind: 'dir', size: 42, children: null },
              'tests':     { kind: 'dir', size: 18, children: null },
              'node_modules': { kind: 'dir', size: 1847, children: null, dim: true },
              'package.json': { kind: 'file', size: '2.1 KB', mtime: '2h ago' },
              'README.md':    { kind: 'file', size: '4.8 KB', mtime: '3d ago' },
              '.env':         { kind: 'file', size: '612 B', mtime: '1d ago', secret: true },
            }
          }])),
        },
        'agent-workspaces': {
          kind: 'dir',
          children: {
            'opus-refactor-7a2f': { kind: 'dir', size: 3, active: true, mtime: 'active · 2m' },
            'sonnet-dev-9c1e':    { kind: 'dir', size: 5, active: true, mtime: 'active · 14m' },
            'codex-tests-4b8a':   { kind: 'dir', size: 2, mtime: 'idle · 1h' },
            'archived':           { kind: 'dir', size: 47, dim: true },
          }
        },
        'logs': {
          kind: 'dir',
          children: {
            'agent.log':   { kind: 'file', size: '14.2 MB', mtime: 'just now', streaming: true },
            'engine.log':  { kind: 'file', size: '3.1 MB', mtime: '12s ago' },
            'system.log':  { kind: 'file', size: '892 KB', mtime: '1m ago' },
            'audit.jsonl': { kind: 'file', size: '5.7 MB', mtime: '4m ago' },
          }
        },
        '.bks': {
          kind: 'dir', dim: true,
          children: {
            'config.toml':    { kind: 'file', size: '1.4 KB', mtime: '2d ago' },
            'credentials':    { kind: 'file', size: '—',       mtime: 'locked', secret: true },
            'engines.lock':   { kind: 'file', size: '812 B',   mtime: '2d ago' },
          }
        },
      }
    }
  };
}

function NodeDetail({ onNav }) {
  const n = NODES.find(x => x.id === window.__currentNode) || NODES[0];
  const [tab, setTab] = React.useState('overview');
  const degraded = n.engines.some(e => e.status !== 'ready');
  const dotColor = n.status !== 'online' ? 'var(--status-blocked)' : degraded ? 'var(--status-working)' : 'var(--status-done)';
  const bound = PROJECTS.filter(p => n.projects.includes(p.id));

  // fake 60-tick load sparkline
  const spark = React.useMemo(() => Array.from({length:60},(_,i)=>{
    const base = Math.sin(i/9 + n.id.length) * 0.5 + 0.5;
    return Math.max(0.08, Math.min(1, base * 0.6 + (i%7===0?0.4:Math.random()*0.35)));
  }), [n.id]);

  return (
    <div className="fade-in node-detail">
      <div className="page-head page-head--compound">
        <div style={{display:'flex',alignItems:'center',gap:14}}>
          <div className={`node-badge ${n.kind} lg`}>
            <Icon name={n.kind==='local'?'terminal':'cloud'} size={22} />
          </div>
          <div>
            <div style={{display:'flex',alignItems:'center',gap:10,flexWrap:'wrap'}}>
              <div className="page-title mono" style={{fontSize:22, whiteSpace:'nowrap'}}>{n.name}</div>
              <span className="node-dot" style={{background: dotColor, width:9, height:9}} />
              <span className="chip chip-done" style={{textTransform:'none'}}>{n.status}</span>
            </div>
            <div className="page-sub">{n.kind==='local'?'Local host':'Cloud host'} · {n.region} · up {n.uptime}</div>
          </div>
        </div>
        <div className="page-actions">
          <button className="ibtn bordered"><Icon name="refresh" size={13} /> Re-scan</button>
          <button className="ibtn bordered" onClick={() => setTab('terminal')}><Icon name="terminal" size={13} /> Open shell</button>
          <button className="ibtn"><Icon name="dots" size={14} /></button>
        </div>
      </div>

      <div className="nd-tabs">
        {NODE_TABS.map(t => (
          <button key={t.id} className={`nd-tab ${tab===t.id?'active':''}`} onClick={() => setTab(t.id)}>
            <Icon name={t.icon} size={13} /> {t.label}
          </button>
        ))}
        <span className="spacer" />
        <span className="mono dim" style={{fontSize:11}}>
          <span className="node-dot" style={{background: dotColor, width:6, height:6, display:'inline-block', marginRight:6, verticalAlign:'middle'}} />
          connected · {n.region}
        </span>
      </div>

      {tab === 'overview' && (
        <div className="nd-grid">
          <div className="nd-main">
            <div className="nd-section">
              <div className="nd-section-head"><div className="nd-section-title">Live load</div><span className="dim mono" style={{fontSize:11}}>last 60 min</span></div>
              <div className="nd-load-kpis">
                {[
                  {k:'cpu',label:'CPU',detail:n.specs.cpu},
                  {k:'ram',label:'Memory',detail:n.specs.ram},
                  {k:'disk',label:'Disk',detail:n.specs.disk},
                ].map(({k,label,detail}) => {
                  const v = n.load[k];
                  const color = v > 75 ? 'var(--status-blocked)' : v > 50 ? 'var(--status-working)' : 'var(--status-done)';
                  return (
                    <div key={k} className="nd-load-kpi">
                      <div className="nd-load-label">{label}</div>
                      <div className="nd-load-val mono" style={{color}}>{v}<span className="dim" style={{fontSize:13}}>%</span></div>
                      <div className="nd-load-detail dim">{detail}</div>
                    </div>
                  );
                })}
              </div>
              <div className="nd-spark">
                {spark.map((v,i)=>(
                  <div key={i} className="nd-spark-bar" style={{height: Math.round(v*100)+'%', background: `color-mix(in oklch, var(--accent) ${Math.round(v*90+10)}%, var(--bg-2))`}} />
                ))}
              </div>
            </div>

            <div className="nd-section">
              <div className="nd-section-head"><div className="nd-section-title">Engines installed</div><span className="dim mono" style={{fontSize:11}}>{n.engines.length} · auto-detected</span></div>
              <div className="nd-engine-list">
                {n.engines.map(e => (
                  <div key={e.id} className="nd-engine">
                    <div className="nd-eng-logo" style={{background: ENGINE_COLORS[e.id]}}>{e.id[0].toUpperCase()}</div>
                    <div style={{flex:1,minWidth:0}}>
                      <div className="nd-eng-title">{ENGINE_LABEL[e.id]}</div>
                      <div className="nd-eng-sub mono dim">{e.id} · v{e.version}</div>
                    </div>
                    <div className="nd-eng-metric"><div className="dim">Latency</div><div className="mono">{e.latency}<span className="dim">ms</span></div></div>
                    <div className="nd-eng-metric"><div className="dim">Status</div><div className={`ne-status ${e.status}`}>{e.status}</div></div>
                    <button className="ibtn bordered">Configure</button>
                  </div>
                ))}
              </div>
            </div>

            <div className="nd-section">
              <div className="nd-section-head"><div className="nd-section-title">Recent runs</div><span className="dim mono" style={{fontSize:11}}>{n.runs7d.toLocaleString()} · 7d</span></div>
              <table className="nd-table">
                <thead><tr><th>Issue</th><th>Agent</th><th>Engine</th><th>Duration</th><th style={{textAlign:'right'}}>Started</th></tr></thead>
                <tbody>
                  {ISSUES.slice(0,5).map((iss,i) => (
                    <tr key={iss.id}>
                      <td><span className="mono dim" style={{fontSize:11}}>{iss.id}</span> &nbsp; {iss.title}</td>
                      <td><span className="mono" style={{fontSize:11}}>sonnet-dev</span></td>
                      <td><span className="ne-logo" style={{background: ENGINE_COLORS[n.engines[i%n.engines.length].id], display:'inline-grid'}}>{n.engines[i%n.engines.length].id[0].toUpperCase()}</span> <span className="dim" style={{fontSize:11}}>{ENGINE_LABEL[n.engines[i%n.engines.length].id]}</span></td>
                      <td className="mono dim">{(2 + (i*1.7)%8).toFixed(1)}s</td>
                      <td style={{textAlign:'right'}} className="dim">{i===0?'just now':`${i*4+2}m ago`}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>

          <aside className="nd-side">
            <div className="nd-section">
              <div className="nd-section-title">Specs</div>
              <div className="nd-kv"><div className="dim">Host type</div><div className="mono">{n.kind}</div></div>
              <div className="nd-kv"><div className="dim">Region</div><div className="mono">{n.region}</div></div>
              <div className="nd-kv"><div className="dim">Uptime</div><div className="mono">{n.uptime}</div></div>
              <div className="nd-kv"><div className="dim">CPU</div><div className="mono">{n.specs.cpu}</div></div>
              <div className="nd-kv"><div className="dim">RAM</div><div className="mono">{n.specs.ram}</div></div>
              <div className="nd-kv"><div className="dim">Disk</div><div className="mono">{n.specs.disk}</div></div>
            </div>

            <div className="nd-section">
              <div className="nd-section-title">Bound projects <span className="dim mono" style={{fontSize:11,fontWeight:500,marginLeft:6}}>{bound.length}</span></div>
              {bound.map(p => (
                <div key={p.id} className="nd-proj" onClick={() => { window.__currentProj = p.id; onNav && onNav('project-detail'); }}>
                  <span className="pc-dot" style={{background: p.color}} />
                  <div style={{flex:1,minWidth:0}}>
                    <div className="nd-proj-name">{p.name}</div>
                    <div className="mono dim" style={{fontSize:11}}>{p.id}</div>
                  </div>
                  <Icon name="chevron-right" size={13} className="dim" />
                </div>
              ))}
            </div>

            <div className="nd-section">
              <div className="nd-section-title">Policy</div>
              <div className="nd-kv"><div className="dim">Max concurrent</div><div className="mono">{n.kind==='local'?'3':'12'}</div></div>
              <div className="nd-kv"><div className="dim">Auto-approve</div><div className="mono">read-only</div></div>
              <div className="nd-kv"><div className="dim">Network</div><div className="mono">allowlist · 14</div></div>
              <div className="nd-kv"><div className="dim">Secrets</div><div className="mono">vault:{n.id}</div></div>
            </div>
          </aside>
        </div>
      )}

      {tab === 'terminal' && <NodeTerminal n={n} />}
      {tab === 'files' && <NodeFiles n={n} />}
    </div>
  );
}

// ---- Node Terminal ----
const TERMINAL_SESSIONS = [
  { id: 'sh-1', title: 'zsh', user: 'ming', cwd: '~' },
  { id: 'sh-2', title: 'engine · claude', user: 'bks', cwd: '~/agent-workspaces/opus-refactor-7a2f', agent: true },
  { id: 'sh-3', title: 'tail agent.log', user: 'bks', cwd: '~/logs', readonly: true },
];

function NodeTerminal({ n }) {
  const [activeId, setActiveId] = React.useState('sh-1');
  const [input, setInput] = React.useState('');
  const session = TERMINAL_SESSIONS.find(s => s.id === activeId);

  // Build a history per session
  const history = React.useMemo(() => {
    if (session.id === 'sh-1') return [
      { type: 'prompt', cwd: '~', cmd: 'bks node info' },
      { type: 'out', lines: [
        `Node: ${n.name}`,
        `Kind: ${n.kind}   Region: ${n.region}`,
        `Uptime: ${n.uptime}   Runs (7d): ${n.runs7d}`,
        `Engines: ${n.engines.map(e=>`${e.id}@${e.version}`).join(', ')}`,
      ]},
      { type: 'prompt', cwd: '~', cmd: 'ls ~/projects' },
      { type: 'out', grid: PROJECTS.filter(p=>n.projects.includes(p.id)).map(p=>p.slug) },
      { type: 'prompt', cwd: '~', cmd: 'top -b -n 1 | head -6' },
      { type: 'out', lines: [
        `top - 14:32:01 up ${n.uptime}, 2 users, load avg: ${(n.load.cpu/100*4).toFixed(2)}, ${(n.load.cpu/110*4).toFixed(2)}, ${(n.load.cpu/120*4).toFixed(2)}`,
        `Tasks: 247 total,   3 running, 244 sleeping`,
        `%Cpu(s): ${n.load.cpu.toFixed(1)} us,  ${(n.load.cpu*0.3).toFixed(1)} sy,  0.0 ni, ${(100-n.load.cpu*1.3).toFixed(1)} id`,
        `MiB Mem :   total,  ${(100-n.load.ram).toFixed(0)}% free,  ${n.load.ram}% used`,
      ]},
      { type: 'prompt', cwd: '~', cmd: '' },
    ];
    if (session.id === 'sh-2') return [
      { type: 'system', text: `[engine] Connected to claude-code v${n.engines.find(e=>e.id==='claude')?.version || '0.8.2'}` },
      { type: 'system', text: `[engine] Attached to session opus-refactor-7a2f · project ATLAS` },
      { type: 'prompt', cwd: session.cwd, cmd: 'npm test -- --watch=false' },
      { type: 'out', lines: [
        ' PASS  src/ratelimit.test.ts',
        ' PASS  src/stream.test.ts',
        ' FAIL  src/auth.test.ts',
        '   ● token refresh › should retry on 401',
        '     expected 200, got 401 (after 3 retries)',
        '',
        'Tests: 2 passed, 1 failed, 3 total',
      ], error: true },
      { type: 'prompt', cwd: session.cwd, cmd: 'bks agent handoff --to sonnet-dev --reason "tests failing"' },
      { type: 'out', lines: [
        '✓ Handoff queued. sonnet-dev will pick up at next checkpoint.',
        '  → workspace preserved at ~/agent-workspaces/opus-refactor-7a2f',
      ]},
      { type: 'prompt', cwd: session.cwd, cmd: '' },
    ];
    // sh-3: tailing log
    return [
      { type: 'system', text: `tailing ~/logs/agent.log · readonly · streaming` },
      { type: 'log', time: '14:32:01', level: 'INFO', source: 'opus-review', msg: 'Reviewing PR #412 — 18 files, 847 lines' },
      { type: 'log', time: '14:32:03', level: 'INFO', source: 'opus-review', msg: 'Ran skill:review-security (0.8s) — 2 findings' },
      { type: 'log', time: '14:32:04', level: 'WARN', source: 'opus-review', msg: 'Found hardcoded token in src/config.ts:42' },
      { type: 'log', time: '14:32:05', level: 'INFO', source: 'sonnet-dev', msg: 'Opened issue HLX-217 — extracting stripe webhook logic' },
      { type: 'log', time: '14:32:07', level: 'INFO', source: 'sonnet-dev', msg: 'Ran tool:read_file(src/billing/webhook.ts)' },
      { type: 'log', time: '14:32:09', level: 'INFO', source: 'sonnet-dev', msg: 'Ran tool:write_file(src/billing/webhook.ts) · +34 −12' },
      { type: 'log', time: '14:32:11', level: 'ERROR', source: 'codex-tests', msg: 'Engine degraded · gemini latency 820ms (budget 500ms)' },
      { type: 'log', time: '14:32:12', level: 'INFO', source: 'opus-review', msg: 'Posted review on PR #412 — requested changes' },
      { type: 'log', time: '14:32:14', level: 'INFO', source: 'sonnet-dev', msg: 'Ran tool:bash(npm test) · exit 1' },
    ];
  }, [session.id, n.id]);

  return (
    <div className="nd-term-wrap">
      <div className="nd-term">
        <div className="nd-term-head">
          <div className="traffic"><span /><span /><span /></div>
          <div className="nd-term-tabs">
            {TERMINAL_SESSIONS.map(s => (
              <button key={s.id} className={`nd-term-tab ${activeId===s.id?'active':''}`} onClick={() => setActiveId(s.id)}>
                {s.agent && <span className="nd-term-tab-dot agent" />}
                {s.readonly && <Icon name="eye" size={11} />}
                {!s.agent && !s.readonly && <Icon name="terminal" size={11} />}
                {s.title}
              </button>
            ))}
            <button className="nd-term-tab plus">+</button>
          </div>
          <span className="spacer" />
          <span className="mono dim" style={{fontSize:11}}>{session.user}@{n.name}</span>
        </div>

        <div className="nd-term-body">
          {history.map((h, i) => {
            if (h.type === 'system') return <div key={i} className="t-sys">{h.text}</div>;
            if (h.type === 'prompt') return (
              <div key={i} className="t-line">
                <span className="t-prompt">
                  <span className="t-user">{session.user}</span>
                  <span className="t-at">@</span>
                  <span className="t-host">{n.name}</span>
                  <span className="t-cwd">{h.cwd}</span>
                  <span className="t-sym">$</span>
                </span>
                {h.cmd ? <span className="t-cmd">{h.cmd}</span> : <span className="t-cursor">▊</span>}
              </div>
            );
            if (h.type === 'out') {
              if (h.grid) return (
                <div key={i} className="t-out t-grid">
                  {h.grid.map(g => <span key={g} className="t-dir">{g}/</span>)}
                </div>
              );
              return (
                <div key={i} className={`t-out ${h.error?'err':''}`}>
                  {h.lines.map((l,j) => <div key={j}>{l}</div>)}
                </div>
              );
            }
            if (h.type === 'log') return (
              <div key={i} className="t-log">
                <span className="t-log-time">{h.time}</span>
                <span className={`t-log-level l-${h.level}`}>{h.level}</span>
                <span className="t-log-src">{h.source}</span>
                <span className="t-log-msg">{h.msg}</span>
              </div>
            );
            return null;
          })}
        </div>

        <div className="nd-term-input">
          <span className="t-prompt">
            <span className="t-user">{session.user}</span>
            <span className="t-at">@</span>
            <span className="t-host">{n.name}</span>
            <span className="t-cwd">{session.cwd}</span>
            <span className="t-sym">$</span>
          </span>
          <input
            value={input}
            onChange={e => setInput(e.target.value)}
            placeholder={session.readonly ? 'readonly tail — press Ctrl+C to exit' : 'Type a command…'}
            disabled={session.readonly}
            autoFocus
          />
          {!session.readonly && (
            <div className="nd-term-hint">
              <kbd>⌘K</kbd> commands · <kbd>Tab</kbd> complete · <kbd>Ctrl+C</kbd> cancel
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ---- Node Files ----
function NodeFiles({ n }) {
  const fs = React.useMemo(() => buildFS(n), [n.id]);
  const [path, setPath] = React.useState(['~']);
  const [selected, setSelected] = React.useState(null);

  // Walk fs tree to current path
  let cwd = fs['~'];
  for (let i = 1; i < path.length; i++) {
    cwd = cwd.children?.[path[i]];
    if (!cwd) break;
  }
  const entries = cwd && cwd.children
    ? Object.entries(cwd.children).map(([name, e]) => ({ name, ...e }))
    : [];

  const go = (name) => {
    const e = cwd.children?.[name];
    if (e?.kind === 'dir' && e.children) setPath([...path, name]);
    else setSelected(name);
  };

  const up = () => { if (path.length > 1) setPath(path.slice(0,-1)); };
  const crumb = (i) => setPath(path.slice(0, i+1));

  const selectedEntry = selected ? cwd.children?.[selected] : null;

  return (
    <div className="nd-files">
      <div className="nd-fb-head">
        <div className="nd-fb-crumbs">
          <button className="ibtn" onClick={up} disabled={path.length<=1}><Icon name="chevron-right" size={12} style={{transform:'rotate(180deg)'}} /></button>
          {path.map((p, i) => (
            <React.Fragment key={i}>
              <button className={`nd-crumb ${i===path.length-1?'active':''}`} onClick={() => crumb(i)}>{p}</button>
              {i < path.length-1 && <span className="dim">/</span>}
            </React.Fragment>
          ))}
        </div>
        <span className="spacer" />
        <div className="search sm">
          <Icon name="search" size={12} className="dim" />
          <input placeholder="Search files…" />
        </div>
        <button className="ibtn bordered"><Icon name="upload" size={12} /> Upload</button>
        <button className="ibtn bordered"><Icon name="plus" size={12} /> New</button>
      </div>

      <div className="nd-fb-grid">
        <div className="nd-fb-list">
          <div className="nd-fb-row head">
            <span className="nd-fb-name">Name</span>
            <span className="nd-fb-size">Size</span>
            <span className="nd-fb-time">Modified</span>
            <span className="nd-fb-owner">Owner</span>
          </div>
          {entries.length === 0 && <div className="nd-fb-empty dim">empty directory</div>}
          {entries.map(e => (
            <div
              key={e.name}
              className={`nd-fb-row ${selected===e.name?'sel':''} ${e.dim?'dim-row':''}`}
              onClick={() => { setSelected(e.name); }}
              onDoubleClick={() => go(e.name)}
            >
              <span className="nd-fb-name">
                <Icon name={e.kind==='dir'?'folder':'file'} size={14} style={{color: e.kind==='dir' ? 'var(--accent)' : 'var(--fg-3)'}} />
                <span style={{fontWeight: e.kind==='dir'?500:400}}>{e.name}{e.kind==='dir'?'/':''}</span>
                {e.active && <span className="fb-badge active">active</span>}
                {e.streaming && <span className="fb-badge live">live</span>}
                {e.secret && <span className="fb-badge secret">secret</span>}
                {e.owner && <span className="fb-badge own">{e.owner}</span>}
              </span>
              <span className="nd-fb-size mono dim">{e.kind==='dir'? (e.size ? `${e.size} items` : '—') : e.size}</span>
              <span className="nd-fb-time dim">{e.mtime || '—'}</span>
              <span className="nd-fb-owner mono dim">{e.kind==='dir' ? (e.owner ? 'project' : 'bks') : (e.secret?'vault':'bks')}</span>
            </div>
          ))}
        </div>

        <aside className="nd-fb-preview">
          {!selectedEntry && <div className="nd-fb-empty dim" style={{padding:32,textAlign:'center'}}>Select a file or folder to preview</div>}
          {selectedEntry && (
            <>
              <div className="nd-fb-prev-head">
                <Icon name={selectedEntry.kind==='dir'?'folder':'file'} size={22} style={{color: selectedEntry.kind==='dir' ? 'var(--accent)' : 'var(--fg-3)'}} />
                <div style={{flex:1,minWidth:0}}>
                  <div className="nd-fb-prev-name">{selected}</div>
                  <div className="mono dim" style={{fontSize:11}}>{[...path, selected].join('/')}</div>
                </div>
              </div>
              <div className="nd-fb-kv"><span className="dim">Size</span><span className="mono">{selectedEntry.size || (selectedEntry.kind==='dir'?'—':'—')}</span></div>
              <div className="nd-fb-kv"><span className="dim">Kind</span><span className="mono">{selectedEntry.kind === 'dir' ? 'directory' : selected.split('.').pop()}</span></div>
              <div className="nd-fb-kv"><span className="dim">Modified</span><span>{selectedEntry.mtime || '—'}</span></div>
              <div className="nd-fb-kv"><span className="dim">Permissions</span><span className="mono">{selectedEntry.secret?'0600':'0644'}</span></div>
              {selectedEntry.kind === 'file' && selected.endsWith('.json') && (
                <pre className="nd-fb-code">{`{
  "name": "atlas-api",
  "version": "2.4.1",
  "engines": { "node": ">=20" },
  "dependencies": {
    "fastify": "^4.26.0",
    "zod": "^3.22.4"
  }
}`}</pre>
              )}
              {selectedEntry.kind === 'file' && (selected.endsWith('.log') || selected.endsWith('.jsonl')) && (
                <pre className="nd-fb-code log">
{`14:32:01  INFO  opus-review     Reviewing PR #412 — 18 files, 847 lines
14:32:03  INFO  opus-review     Ran skill:review-security (0.8s) — 2 findings
14:32:04  WARN  opus-review     Found hardcoded token in src/config.ts:42
14:32:05  INFO  sonnet-dev      Opened issue HLX-217
14:32:07  INFO  sonnet-dev      Ran tool:read_file · src/billing/webhook.ts
14:32:09  INFO  sonnet-dev      Ran tool:write_file · +34 −12`}
                </pre>
              )}
              {selectedEntry.secret && (
                <div className="nd-fb-secret">
                  <Icon name="lock" size={13} />
                  Contents hidden — sealed in vault · <a className="link">request access</a>
                </div>
              )}
              <div className="nd-fb-actions">
                {selectedEntry.kind === 'file' && !selectedEntry.secret && <button className="ibtn bordered"><Icon name="external" size={12} /> Open</button>}
                <button className="ibtn bordered"><Icon name="file" size={12} /> Copy path</button>
                {selectedEntry.kind === 'file' && <button className="ibtn bordered">Download</button>}
              </div>
            </>
          )}
        </aside>
      </div>
    </div>
  );
}

// ---- Knowledge base ----
function KnowledgePage({ onNav, role }) {
  const [query, setQuery] = React.useState('');
  const [cat, setCat] = React.useState('all');
  const [sort, setSort] = React.useState('pinned');

  // Visible set based on role — regular users only see verified entries,
  // and only those touching projects they're a member of.
  const MY_PROJECTS = ['ATLAS','NOVA']; // demo: regular user is on Atlas + Nova
  const visibleKB = role === 'member'
    ? KNOWLEDGE.filter(k => k.verified && k.projects.some(p => MY_PROJECTS.includes(p)))
    : KNOWLEDGE;
  const visiblePending = role === 'member' ? [] : KNOWLEDGE_PENDING;

  const filtered = visibleKB
    .filter(k => cat === 'all' || k.category === cat)
    .filter(k => {
      if (!query) return true;
      const q = query.toLowerCase();
      return k.title.toLowerCase().includes(q)
        || k.summary.toLowerCase().includes(q)
        || k.tags.some(t => t.includes(q));
    })
    .sort((a, b) => {
      if (sort === 'pinned') return (b.pinned?1:0)-(a.pinned?1:0) || b.uses - a.uses;
      if (sort === 'recent') return a.updated.localeCompare(b.updated);
      if (sort === 'uses')   return b.uses - a.uses;
      return 0;
    });

  // Tag frequency for sidebar
  const tagFreq = {};
  visibleKB.forEach(k => k.tags.forEach(t => tagFreq[t] = (tagFreq[t]||0)+1));
  const topTags = Object.entries(tagFreq).sort((a,b) => b[1]-a[1]).slice(0,10);

  const catMeta = Object.fromEntries(KNOWLEDGE_CATEGORIES.map(c => [c.id, c]));

  return (
    <div className="fade-in">
      <div className="page-head">
        <div>
          <div className="page-title">Knowledge</div>
          <div className="page-sub">
            {role === 'admin'
              ? `Curated from ${visibleKB.length} entries · ${visiblePending.length} agent-proposed pending`
              : `${visibleKB.length} entries relevant to your projects`}
          </div>
        </div>
        <div className="page-actions">
          {role === 'admin' && <button className="ibtn bordered"><Icon name="upload" size={13} /> Import</button>}
          <button className="ibtn primary"><Icon name="plus" size={13} /> New entry</button>
        </div>
      </div>

      <div className="kb-hero">
        <div className="kb-search-wrap">
          <div className="kb-search">
            <Icon name="search" size={16} className="dim" />
            <input placeholder="Search conventions, gotchas, patterns, decisions…" value={query} onChange={e => setQuery(e.target.value)} />
            <kbd>⌘K</kbd>
          </div>
        </div>
        <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
          <button className={`ibtn ${sort==='pinned'?'bordered':''}`} onClick={() => setSort('pinned')}>Top</button>
          <button className={`ibtn ${sort==='recent'?'bordered':''}`} onClick={() => setSort('recent')}>Recent</button>
          <button className={`ibtn ${sort==='uses'?'bordered':''}`} onClick={() => setSort('uses')}>Most used</button>
        </div>
      </div>

      <div className="kb-cat-strip">
        {KNOWLEDGE_CATEGORIES.map(c => {
          const count = visibleKB.filter(k => k.category === c.id).length;
          return (
            <div
              key={c.id}
              className={`kb-cat ${cat===c.id?'active':''}`}
              style={{'--kb-c': c.color}}
              onClick={() => setCat(cat===c.id?'all':c.id)}
            >
              <div className="kb-cat-count mono" style={{color: c.color}}>{count}</div>
              <div className="kb-cat-label">{c.label}</div>
              <div className="kb-cat-desc">{c.desc}</div>
            </div>
          );
        })}
      </div>

      <div className="kb-layout">
        <div className="kb-main">
          {cat !== 'all' && (
            <div className="kb-toolbar">
              <span className="dim" style={{fontSize:12}}>Filtered by</span>
              <span className="kb-cat-tag" style={{'--kb-c': catMeta[cat].color}}>{catMeta[cat].label}</span>
              <button className="ibtn" onClick={() => setCat('all')}>× clear</button>
              <span className="spacer" />
              <span className="dim mono" style={{fontSize:11}}>{filtered.length} entries</span>
            </div>
          )}
          {filtered.length === 0 && (
            <div className="kb-card" style={{textAlign:'center',padding:40}}>
              <div className="dim">No entries match.</div>
            </div>
          )}
          {filtered.map(k => {
            const meta = catMeta[k.category];
            return (
              <div key={k.id} className={`kb-card ${k.pinned?'pinned':''}`} style={{'--kb-c': meta.color}}>
                <div className="kb-card-head">
                  <span className="kb-cat-tag">{meta.label}</span>
                  <div className="kb-title">{k.title}</div>
                  {k.pinned && <Icon name="bookmark" size={14} className="kb-pinned-mark" />}
                  {k.verified && <span className="kb-verified"><Icon name="check" size={10} /> verified</span>}
                  {!k.verified && <span className="kb-pending-chip">unverified</span>}
                </div>
                <div className="kb-summary">{k.summary}</div>
                <div className="kb-foot">
                  {k.tags.map(t => <span key={t} className="kb-tag">#{t}</span>)}
                  <span className="spacer" style={{flex:1}} />
                  {k.sources.slice(0,2).map(s => (
                    <span key={s.issue} className="kb-src" onClick={() => { window.__currentIssue = s.issue; onNav && onNav('issue-detail'); }}>
                      <Icon name="issue" size={10} /> {s.issue}
                    </span>
                  ))}
                  {k.sources.length > 2 && <span className="dim" style={{fontSize:11}}>+{k.sources.length-2} more</span>}
                  <span className="dim mono" style={{fontSize:11}}>{k.uses} uses</span>
                  <span className="dim" style={{fontSize:11}}>· {k.updated}</span>
                </div>
                <div style={{display:'flex',alignItems:'center',gap:6,marginTop:8,paddingTop:8,borderTop:'1px dashed var(--border-subtle)',fontSize:11}}>
                  <span className="dim">by</span>
                  {k.author.kind === 'human' ? (
                    <>
                      <span className="avatar" style={{width:16,height:16,fontSize:9,background:k.author.color}}>{k.author.avatar}</span>
                      <span>{k.author.name}</span>
                    </>
                  ) : (
                    <>
                      <span className="avatar agent" style={{width:16,height:16,fontSize:9,background: ENGINE_COLORS[k.author.engine]}}>{k.author.engine[0].toUpperCase()}</span>
                      <span className="mono">{k.author.name}</span>
                      <span className="dim">· agent</span>
                    </>
                  )}
                  {k.verifiedBy && <><span className="dim">· verified by {k.verifiedBy}</span></>}
                  <span className="spacer" style={{flex:1}} />
                  <span className="dim mono">{k.projects.join(' · ')}</span>
                </div>
              </div>
            );
          })}
        </div>

        <aside className="kb-side">
          {role === 'admin' && visiblePending.length > 0 && (
            <div className="kb-pending">
              <div className="kb-pending-head">
                <Icon name="sparkles" size={14} style={{color:'var(--accent)'}} />
                <div className="kb-pending-title">Agent-proposed</div>
                <span className="spacer" />
                <span className="dim mono" style={{fontSize:11}}>{visiblePending.length}</span>
              </div>
              <div className="dim" style={{fontSize:11,lineHeight:1.5,marginBottom:8}}>
                Auto-extracted from recent issue resolutions. Review to promote into the knowledge base.
              </div>
              {visiblePending.map(p => (
                <div key={p.id} className="kb-pending-item">
                  <div className="kb-pending-text">{p.proposed}</div>
                  <div className="kb-pending-foot">
                    <span className="kb-cat-tag" style={{'--kb-c': catMeta[p.category].color, fontSize:9.5, padding:'2px 6px'}}>{catMeta[p.category].label}</span>
                    <span className="mono dim">{p.extractedBy}</span>
                    <span className="spacer" style={{flex:1}} />
                    <span className="dim">conf</span>
                    <div className="kb-conf-bar"><div className="kb-conf-fill" style={{width: (p.confidence*100)+'%'}} /></div>
                    <span className="mono dim">{Math.round(p.confidence*100)}%</span>
                  </div>
                  <div style={{fontSize:11,marginTop:6}}>
                    <span className="dim">from </span>
                    <span className="kb-src" onClick={() => { window.__currentIssue = p.source.issue; onNav && onNav('issue-detail'); }}>
                      <Icon name="issue" size={10} /> {p.source.issue}
                    </span>
                    <span className="dim"> · {p.source.title}</span>
                  </div>
                  <div className="kb-pending-actions">
                    <button className="ibtn primary" style={{flex:1,justifyContent:'center'}}>Promote</button>
                    <button className="ibtn bordered">Edit</button>
                    <button className="ibtn">Dismiss</button>
                  </div>
                </div>
              ))}
            </div>
          )}

          <div className="kb-aside-section">
            <div className="kb-aside-title">Top tags</div>
            {topTags.map(([t,n]) => (
              <div key={t} className="kb-top-tag" onClick={() => setQuery(t)} style={{cursor:'pointer'}}>
                <span className="mono">#{t}</span>
                <span className="dim mono" style={{fontSize:11}}>{n}</span>
              </div>
            ))}
          </div>

          <div className="kb-aside-section">
            <div className="kb-aside-title">How it works</div>
            <div style={{fontSize:12,lineHeight:1.6,color:'var(--fg-2)'}}>
              <div style={{display:'flex',gap:8,marginBottom:6}}>
                <span className="mono dim" style={{flex:'0 0 14px'}}>1</span>
                <span>Agents and reviewers flag useful findings during issue resolution.</span>
              </div>
              <div style={{display:'flex',gap:8,marginBottom:6}}>
                <span className="mono dim" style={{flex:'0 0 14px'}}>2</span>
                <span><span className="mono">opus-review</span> extracts candidates, scored by confidence.</span>
              </div>
              <div style={{display:'flex',gap:8,marginBottom:6}}>
                <span className="mono dim" style={{flex:'0 0 14px'}}>3</span>
                <span>A human verifies and promotes. Agents then cite entries in future runs.</span>
              </div>
            </div>
          </div>
        </aside>
      </div>
    </div>
  );
}

Object.assign(window, { NodeDetail, NodeTerminal, NodeFiles, KnowledgePage });
