// Lynxe shell — sidebar (collapsible), topbar, primitives

const { useState, useEffect, useRef, useCallback } = React;

function Sidebar({ active, onNav, collapsed, onToggleCollapse, onSearch, onNotifications, unread }) {
  const items = [
    { id: 'dashboard', label: 'Dashboard', icon: I.Compass },
    { id: 'today',     label: 'Today',     icon: I.Today },
    { id: 'notes',     label: 'Notes',     icon: I.Note },
    { id: 'classroom', label: 'Classroom', icon: I.Class },
    { id: 'missions',  label: 'Missions',  icon: I.Mission },
  ];

  return (
    <aside className="sidebar">
      <div className="sb-head">
        <div className="brand" onClick={onToggleCollapse} title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}>
          <div className="brand-mark"><LynxeMark /></div>
          <div className="brand-name">Lynxe</div>
          {!collapsed && <I.ChevronUpDown size={11} className="brand-chev" />}
        </div>
        {!collapsed && (
          <button className="icon-btn" title="Notifications" onClick={onNotifications} style={{ position: 'relative' }}>
            <I.Bell size={13} />
            {unread > 0 && <span style={{ position: 'absolute', top: 2, right: 2, width: 5, height: 5, borderRadius: 3, background: 'var(--danger)' }} />}
          </button>
        )}
        {!collapsed && (
          <button className="icon-btn" title="Collapse sidebar" onClick={onToggleCollapse}>
            <I.SidebarCollapse size={13} />
          </button>
        )}
      </div>

      <div className="sb-search" onClick={onSearch}>
        <I.Search size={12} />
        <input placeholder="Search…" readOnly onFocus={(e) => { e.target.blur(); onSearch && onSearch(); }} />
        <span className="kbd">⌘K</span>
      </div>

      <nav className="nav">
        {items.map(it => (
          <div
            key={it.id}
            className={`nav-item ${active === it.id ? 'active' : ''}`}
            onClick={() => onNav(it.id)}
            title={collapsed ? it.label : undefined}
          >
            <span className="nav-ico"><it.icon size={14} /></span>
            <span className="nav-label">{it.label}</span>
            {it.count != null && <span className="nav-count">{it.count}</span>}
          </div>
        ))}
      </nav>

      <div className="sb-foot">
        <div className="sb-foot-user" onClick={() => onNav('account')}>
          <div className="user-mark">SY</div>
          <div className="sb-foot-text ellipsis">Sahishnu Y.</div>
        </div>
        <button
          className={`icon-btn sb-foot-settings ${active === 'settings' ? 'active' : ''}`}
          onClick={() => onNav('settings')}
          title="Settings"
        >
          <I.Settings size={13} />
        </button>
      </div>
    </aside>
  );
}

function LynxeMark() {
  // Two diagonal strokes echoing an "L" + lynx-eye crispness; works on light or dark mark backgrounds
  return (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
      <path d="M5 4 L5 19 L19 19" />
      <path d="M19 5 L13 11" />
    </svg>
  );
}

function Topbar({ crumbs = [], actions, onToggleCollapse, collapsed }) {
  return (
    <div className="topbar">
      <div className="topbar-left">
        {collapsed && (
          <button className="icon-btn" onClick={onToggleCollapse} title="Expand sidebar">
            <I.SidebarExpand size={13} />
          </button>
        )}
        <div className="crumbs">
          {crumbs.map((c, i) => (
            <React.Fragment key={i}>
              {i > 0 && <span className="crumb-sep">/</span>}
              <span className={i === crumbs.length - 1 ? 'c-last' : ''}>{c}</span>
            </React.Fragment>
          ))}
        </div>
      </div>
      <div className="tb-actions">{actions}</div>
    </div>
  );
}

function SubjectTag({ subject, withName = true, size = 6 }) {
  const s = SUBJ_BY_ID[subject];
  if (!s) return null;
  return (
    <span className="subj-tag">
      <span className="subj-dot" style={{ background: s.hex, width: size, height: size }} />
      {withName && (s.short || s.name)}
    </span>
  );
}
function SubjectBar({ subject }) {
  const s = SUBJ_BY_ID[subject];
  return <div className="subj-bar" style={{ background: s ? s.hex : 'transparent' }} />;
}

function Badge({ children }) {
  return <span className="badge">{children}</span>;
}

function Checkbox({ checked, onChange }) {
  return (
    <span
      className={`checkbox ${checked ? 'checked' : ''}`}
      onClick={(e) => { e.stopPropagation(); onChange(!checked); }}
    >
      {checked && <I.Check size={9} strokeWidth={3} />}
    </span>
  );
}

function Toggle({ on, onChange }) {
  return <span className={`toggle ${on ? 'on' : ''}`} onClick={() => onChange(!on)} />;
}

function NotionChip({ title, onClick }) {
  return (
    <a className="notion-chip" onClick={(e) => { e.stopPropagation(); onClick && onClick(); }} title="Open in Notion">
      <span className="nc-mark">N</span>
      <span className="ellipsis" style={{ maxWidth: 200 }}>{title}</span>
      <I.External size={9} />
    </a>
  );
}

function ChatThread({ messages, streamingIndex }) {
  const ref = useRef(null);
  useEffect(() => { if (ref.current) ref.current.scrollTop = ref.current.scrollHeight; }, [messages, streamingIndex]);
  return (
    <div ref={ref} style={{ flex: 1, overflowY: 'auto', padding: '12px 12px 4px', display: 'flex', flexDirection: 'column', gap: 8 }}>
      {messages.map((m, i) => (
        <div key={i} className={`msg ${m.role}`}>
          {m.role === 'ai' && <div className="msg-meta">{m.label || 'AI'}</div>}
          <span>{m.text}</span>
          {streamingIndex === i && <span className="cursor-blink" />}
        </div>
      ))}
    </div>
  );
}

function ChatInput({ placeholder = 'Ask anything…', onSend, disabled }) {
  const [val, setVal] = useState('');
  const ta = useRef(null);
  const submit = () => {
    if (!val.trim() || disabled) return;
    onSend(val.trim());
    setVal('');
    if (ta.current) ta.current.style.height = 'auto';
  };
  const onInput = (e) => {
    setVal(e.target.value);
    e.target.style.height = 'auto';
    e.target.style.height = Math.min(100, e.target.scrollHeight) + 'px';
  };
  return (
    <div className="chat-input-wrap">
      <div className="chat-input">
        <textarea
          ref={ta}
          value={val}
          rows={1}
          placeholder={placeholder}
          onChange={onInput}
          onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submit(); } }}
        />
        <button className="chat-send" disabled={!val.trim() || disabled} onClick={submit}>
          <I.Send size={11} strokeWidth={2.4} />
        </button>
      </div>
    </div>
  );
}

function useFakeChat(initial) {
  const [msgs, setMsgs] = useState(initial || []);
  const [streamIdx, setStreamIdx] = useState(-1);
  const respond = useCallback((reply) => {
    setMsgs(m => [...m, { role: 'ai', label: 'Lynxe', text: '' }]);
    let i = 0;
    setTimeout(() => {
      setMsgs(m => { setStreamIdx(m.length - 1); return m; });
      const tick = () => {
        i += Math.max(2, Math.floor(Math.random() * 5));
        setMsgs(m => {
          const next = [...m];
          next[m.length - 1] = { ...m[m.length - 1], text: reply.slice(0, i) };
          return next;
        });
        if (i < reply.length) setTimeout(tick, 22);
        else setStreamIdx(-1);
      };
      setTimeout(tick, 180);
    }, 0);
  }, []);
  const send = useCallback((text) => {
    setMsgs(m => [...m, { role: 'user', text }]);
    setTimeout(() => respond(canReply(text)), 280);
  }, [respond]);
  return [msgs, send, streamIdx];
}

function canReply(text) {
  const t = text.toLowerCase();
  if (t.includes('roadmap') || t.includes('plan')) return "Updated the roadmap. Cross-product practice moved to tomorrow, flashcards pushed to Thursday — your last past-paper score on cross-product was 62%, so it needs more time. Want me to also add a 20-min recap on the right-hand rule?";
  if (t.includes('volume') || t.includes('add')) return "Adding a new volume on \"Common HSC traps in vector proofs.\" I'll generate it from your last three lecture transcripts plus the 2019–2023 past papers. Ready in about 30 seconds.";
  if (t.includes('summari')) return "Dot product measures alignment, cross product measures spread (with direction). Use dot for angles, projections, work; use cross for area, torque, normal vectors. Want me to turn this into a flashcard?";
  if (t.includes('approach') || t.includes('start') || t.includes('how')) return "Map each question to a single technique. Q1–Q6 are projection-style, so revise Vol 2 first. Q7–Q11 are cross-product. Q12–Q14 are proofs — don't touch those until you've done the practice run.";
  return "Got it. I'll keep this in mind for the rest of the mission. Anything else?";
}

Object.assign(window, { Sidebar, Topbar, SubjectTag, SubjectBar, Badge, Checkbox, Toggle, NotionChip, ChatThread, ChatInput, useFakeChat });
