// Shell.jsx — app chrome: sidebar, topbars, mobile nav, menus, quick capture

function NavWidget({ route, go, onCapture, dueCount, profile }) {
  const counts = { today: dueCount, topics: TOPICS.length, materials: MATERIALS.length };
  const p = profile || { name: 'Avery Reyes', avatar: null };
  const posRef = React.useRef({ x: 20, y: 88 });
  const [, force] = React.useReducer((x) => x + 1, 0);
  const [open, setOpen] = React.useState(true);
  const [docked, setDocked] = React.useState(() => {try {return localStorage.getItem('pl-docked') !== '0';} catch (e) {return true;}});
  React.useEffect(() => {document.body.classList.toggle('nav-docked', docked);try {localStorage.setItem('pl-docked', docked ? '1' : '0');} catch (e) {}return () => document.body.classList.remove('nav-docked');}, [docked]);
  // Briefly light up the Ideas item when a note is captured into the idea board.
  const [flashIdeas, setFlashIdeas] = React.useState(false);
  React.useEffect(() => {
    const onAdd = () => { setFlashIdeas(false); requestAnimationFrame(() => setFlashIdeas(true)); window.clearTimeout(onAdd._t); onAdd._t = window.setTimeout(() => setFlashIdeas(false), 1100); };
    window.addEventListener('pl-idea-added', onAdd);
    return () => window.removeEventListener('pl-idea-added', onAdd);
  }, []);
  const onDown = (e) => {
    if (e.target.closest('.nw-x')) return;
    e.preventDefault();
    const sx = e.clientX,sy = e.clientY,ox = posRef.current.x,oy = posRef.current.y;
    const move = (ev) => {
      const x = Math.max(8, Math.min(window.innerWidth - 248, ox + ev.clientX - sx));
      const y = Math.max(8, Math.min(window.innerHeight - 60, oy + ev.clientY - sy));
      posRef.current = { x, y };force();
    };
    const up = () => {document.removeEventListener('pointermove', move);document.removeEventListener('pointerup', up);};
    document.addEventListener('pointermove', move);document.addEventListener('pointerup', up);
  };
  if (docked) {
    return (
      <aside className="nav-rail">
        <button className="rail-btn primary" onClick={onCapture} title="Quick capture" aria-label="Quick capture"><Icon name="plus" size={20} /></button>
        <div className="nw-sep" style={{ width: 28, margin: '4px 0' }} />
        {NAV.map((n) =>
        <button key={n.id} className={'rail-btn' + (route === n.id ? ' on' : '') + (flashIdeas && n.id === 'ideas' ? ' nav-flash' : '')} onClick={() => go(n.id)} title={n.label} aria-label={n.label}><Icon name={n.icon} size={20} /></button>
        )}
        <div style={{ flex: 1 }} />
        <button className={'rail-btn' + (route === 'settings' ? ' on' : '')} onClick={() => go('settings')} title="Settings" aria-label="Settings"><Icon name="settings" size={20} /></button>
        <button className="rail-btn" onClick={() => go('settings')} title={p.name} aria-label={p.name} style={{ padding: 0 }}>
          <span className="avatar" style={{ width: 32, height: 32, fontSize: 12.5, ...(p.avatar ? { backgroundImage: `url(${p.avatar})`, backgroundSize: 'cover', backgroundPosition: 'center', color: 'transparent' } : {}) }}>{initialsOf(p.name)}</span>
        </button>
        <button className="rail-btn" onClick={() => setDocked(false)} title="Undock menu" aria-label="Undock menu"><Icon name="chevron-right" size={18} /></button>
      </aside>);

  }
  return (
    <aside className="nav-widget" style={{ left: posRef.current.x, top: posRef.current.y }}>
      <div className="nw-head" onPointerDown={onDown}>
        <span className="cw-title"></span>
        <button className="cw-x nw-x" onClick={() => setDocked(true)} title="Dock to left side" style={{ borderRadius: "5px" }}><Icon name="x" size={15} /></button>
      </div>
      {open &&
      <div className="nw-body">
          <button className="btn primary block" style={{ marginBottom: 8 }} onClick={onCapture}>
            <Icon name="plus" size={16} /> Quick capture
          </button>
          <nav className="nav">
            {NAV.map((n) =>
          <button key={n.id} className={'nav-item' + (route === n.id ? ' on' : '') + (flashIdeas && n.id === 'ideas' ? ' nav-flash' : '')} onClick={() => go(n.id)}>
                <Icon name={n.icon} size={19} className="nav-ico" />
                <span style={{ flex: 1 }}>{n.label}</span>
                {counts[n.id] != null && <span className="count">{counts[n.id]}</span>}
              </button>
          )}
          </nav>
          <div className="nw-sep" />
          <button className={'nav-item' + (route === 'settings' ? ' on' : '')} onClick={() => go('settings')}>
            <Icon name="settings" size={19} className="nav-ico" /> Settings
          </button>
          <div className="workspace" onClick={() => go('settings')} style={{ marginTop: 8 }}>
            <span className="ava" style={p.avatar ? { backgroundImage: `url(${p.avatar})`, backgroundSize: 'cover', backgroundPosition: 'center', color: 'transparent' } : undefined}>{initialsOf(p.name)}</span>
            <div style={{ lineHeight: 1.25, minWidth: 0 }}>
              <div style={{ fontSize: 13.5, fontWeight: 600 }}>{p.name}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>Free plan</div>
            </div>
            <Icon name="chevron-right" size={15} style={{ marginLeft: 'auto', color: 'var(--ink-3)' }} />
          </div>
        </div>
      }
    </aside>);

}

function NotifMenu({ onClose, go, onNav }) {
  return (
    <React.Fragment>
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 55 }} />
      <div className="menu" style={{ minWidth: 300, padding: 0, overflow: 'hidden' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '13px 15px', borderBottom: '1px solid var(--line)' }}>
          <span style={{ fontWeight: 600, fontSize: 14 }}>Notifications</span>
          <span className="mono" style={{ fontSize: 11, color: 'var(--blue-ink)' }}>{NOTIFS.filter((n) => n.unread).length} new</span>
        </div>
        <div style={{ maxHeight: 340, overflowY: 'auto', padding: 6 }}>
          {NOTIFS.map((n, i) =>
          <button key={i} type="button" className="notif" onClick={() => { onClose(); onNav ? onNav(n.nav) : go('today'); }} title={'Open ' + n.text}>
              <span className="ndot" style={{ background: n.accent }} />
              <div style={{ lineHeight: 1.35 }}>
                <div style={{ fontSize: 13.5, fontWeight: n.unread ? 600 : 500 }}>{n.text}</div>
                <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2 }}>{n.meta}</div>
              </div>
            </button>
          )}
        </div>
        <button className="btn ghost block" style={{ borderRadius: 0, borderTop: '1px solid var(--line)' }}
        onClick={() => {onClose();go('today');}}>Go to review queue</button>
      </div>
    </React.Fragment>);

}

function SearchResults({ query, onPick, onClose }) {
  const results = searchAll(query);
  const ICONS = { domain: 'grid', topic: 'layers', material: 'cards', note: 'book' };
  const recommended = [
  { kind: 'topic', id: 'caf', title: 'Caffeine', sub: 'The Human Body · Topic', accent: '#C7937D' },
  { kind: 'material', id: 'm-caf-sum', title: 'How caffeine wakes you up', sub: 'Summary · Caffeine', accent: '#C7937D' },
  { kind: 'topic', id: 'tardi', title: 'Tardigrades', sub: 'Biology · Topic', accent: '#2E7A3A' },
  { kind: 'material', id: 'm-simpson-map', title: 'Confounding & aggregation', sub: 'Concept map · Simpson’s Paradox', accent: '#1E8FF0' }];

  const empty = query.trim() === '';
  const list = empty ? recommended : results;
  return (
    <React.Fragment>
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 55 }} />
      <div className="search-panel">
        {!empty && results.length === 0 ?
        <div className="search-empty">No results for “{query}”.</div> :
        <React.Fragment>
            <div className="mh">{empty ? 'Recommended' : results.length + ' result' + (results.length > 1 ? 's' : '')}</div>
            {list.map((r, i) =>
          <button key={i} className="search-item" onClick={() => onPick(r)}>
                <span className="itile" style={{ width: 30, height: 30, borderRadius: 8, background: 'var(--surface-2)',
              color: r.accent || 'var(--ink-3)' }}><Icon name={ICONS[r.kind]} size={16} /></span>
                <span className="si-text">
                  <span className="si-title">{r.title}</span>
                  <span className="si-sub">{r.sub}</span>
                </span>
                <Icon name="arrow-up-right" size={15} style={{ color: 'var(--ink-4)' }} />
              </button>
          )}
          </React.Fragment>
        }
      </div>
    </React.Fragment>);

}

function Topbar({ title, query, setQuery, theme, toggleTheme, onCapture, go, onPick, profile, onNotifNav }) {
  const [notif, setNotif] = React.useState(false);
  const [sOpen, setSOpen] = React.useState(false);
  const pick = (r) => {onPick(r);setQuery('');setSOpen(false);};
  return (
    <header className="topbar">
      <button onClick={() => go('map')} title="Home" style={{ flex: 'none', border: 'none', background: 'transparent', cursor: 'pointer', padding: 0, display: 'flex' }}><Logo size={24} /></button>
      <div style={{ position: 'relative', flex: 1, maxWidth: 420 }}>
        <div className="searchbox" style={{ maxWidth: 'none' }}>
          <button onClick={() => setSOpen(true)} title="Search" style={{ border: 'none', background: 'transparent', padding: 0, cursor: 'pointer', display: 'flex', color: 'var(--ink-3)' }}><Icon name="search" size={16} /></button>
          <input value={query} onChange={(e) => {setQuery(e.target.value);setSOpen(true);}}
          onFocus={() => {if (query) setSOpen(true);}}
          onKeyDown={(e) => {if (e.key === 'Enter') setSOpen(true);if (e.key === 'Escape') setSOpen(false);}}
          placeholder="Search notes, topics, materials…" />
          <button onClick={() => setSOpen(true)} title="Search" style={{ border: 'none', background: 'transparent', padding: 0, cursor: 'pointer', display: 'flex', color: 'var(--ink-3)' }}><Icon name="arrow-right" size={15} /></button>
        </div>
        {sOpen && <SearchResults query={query} onPick={pick} onClose={() => setSOpen(false)} />}
      </div>
      <div className="spacer" />
      <button className="icon-btn primary" onClick={onCapture} title="Quick capture"><Icon name="plus" size={19} /></button>
      <button className="icon-btn" onClick={toggleTheme} title="Toggle theme"><Icon name={theme === 'dark' ? 'sun' : 'moon'} size={18} /></button>
      <div style={{ position: 'relative' }}>
        <button className={'icon-btn' + (notif ? ' active' : '')} onClick={() => setNotif((v) => !v)} title="Notifications">
          <Icon name="bell" size={18} /><span className="badge">{NOTIFS.filter((n) => n.unread).length}</span>
        </button>
        {notif && <NotifMenu onClose={() => setNotif(false)} go={go} onNav={onNotifNav} />}
      </div>
    </header>);

}

function MobileTopbar({ query, setQuery, onCapture, theme, toggleTheme, go, onPick, onNotifNav }) {
  const [search, setSearch] = React.useState(false);
  const [notif, setNotif] = React.useState(false);
  const pick = (r) => {onPick(r);setQuery('');setSearch(false);};
  return (
    <header className="mobile-topbar">
      {search ?
      <div style={{ position: 'relative', flex: 1 }}>
        <div className="searchbox" style={{ maxWidth: 'none' }}>
          <Icon name="search" size={16} style={{ color: 'var(--ink-3)' }} />
          <input autoFocus value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search…" />
          <button className="btn ghost sm" onClick={() => {setSearch(false);setQuery('');}} style={{ padding: 4 }}><Icon name="x" size={16} /></button>
        </div>
        <SearchResults query={query} onPick={pick} onClose={() => {setSearch(false);setQuery('');}} />
      </div> :

      <React.Fragment>
          <button onClick={() => go('map')} title="Home" style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: 0, display: 'flex' }}><Logo size={22} /></button>
          <div className="spacer" />
          <button className="icon-btn" onClick={() => setSearch(true)}><Icon name="search" size={18} /></button>
          <button className="icon-btn" onClick={toggleTheme}><Icon name={theme === 'dark' ? 'sun' : 'moon'} size={18} /></button>
          <div style={{ position: 'relative' }}>
            <button className="icon-btn" onClick={() => setNotif((v) => !v)}><Icon name="bell" size={18} /><span className="badge">{NOTIFS.filter((n) => n.unread).length}</span></button>
            {notif && <div style={{ position: 'absolute', right: 0, top: 46, zIndex: 60 }}><NotifMenu onClose={() => setNotif(false)} go={go} onNav={onNotifNav} /></div>}
          </div>
          <button className="icon-btn primary" onClick={onCapture}><Icon name="plus" size={19} /></button>
        </React.Fragment>
      }
    </header>);

}

function MobileNav({ route, go }) {
  const items = [...NAV, { id: 'settings', label: 'Settings', icon: 'settings' }];
  return (
    <nav className="mobile-nav">
      {items.map((n) =>
      <button key={n.id} className={route === n.id ? 'on' : ''} onClick={() => go(n.id)}>
          <Icon name={n.icon} size={21} stroke={route === n.id ? 2.1 : 1.8} />
          {n.label.split(' ')[0]}
        </button>
      )}
    </nav>);

}

function QuickCapture({ onClose, onSave }) {
  const [text, setText] = React.useState('');
  const [topic, setTopic] = React.useState('');
  const [custom, setCustom] = React.useState('');
  const [adding, setAdding] = React.useState(false);
  // Voice dictation: record speech and append the transcript to the note.
  const [listening, setListening] = React.useState(false);
  const [voiceErr, setVoiceErr] = React.useState('');
  const recRef = React.useRef(null);
  const baseRef = React.useRef('');
  const SR = (typeof window !== 'undefined') && (window.SpeechRecognition || window.webkitSpeechRecognition);
  const stopDictation = () => { try { recRef.current && recRef.current.stop(); } catch (e) {} setListening(false); };
  const startDictation = () => {
    if (!SR) { setVoiceErr('Voice input isn’t supported in this browser.'); return; }
    setVoiceErr('');
    try {
      const rec = new SR();
      rec.lang = 'en-US'; rec.continuous = true; rec.interimResults = true;
      baseRef.current = text ? text.replace(/\s*$/, '') + ' ' : '';
      rec.onresult = (e) => {
        let interim = '', finalAdd = '';
        for (let i = e.resultIndex; i < e.results.length; i++) {
          const tr = e.results[i];
          if (tr.isFinal) finalAdd += tr[0].transcript; else interim += tr[0].transcript;
        }
        if (finalAdd) baseRef.current = (baseRef.current + finalAdd).replace(/\s{2,}/g, ' ');
        setText((baseRef.current + interim).replace(/\s{2,}/g, ' '));
      };
      rec.onerror = (e) => { setVoiceErr(e.error === 'not-allowed' ? 'Microphone access was blocked.' : 'Couldn’t capture audio.'); setListening(false); };
      rec.onend = () => { setListening(false); };
      recRef.current = rec;
      rec.start();
      setListening(true);
    } catch (e) { setVoiceErr('Couldn’t start the microphone.'); setListening(false); }
  };
  React.useEffect(() => () => { try { recRef.current && recRef.current.stop(); } catch (e) {} }, []);
  return (
    <div className="scrim" onMouseDown={(e) => {if (e.target === e.currentTarget) onClose();}}>
      <div className="modal">
        <div className="mhead">
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <MatTile type="summary" size={32} /> <span style={{ fontWeight: 600, fontSize: 16 }}>Quick capture</span>
          </div>
          <button className="icon-btn" style={{ width: 32, height: 32, border: 'none' }} onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="mbody">
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
            <label className="field-label" style={{ marginBottom: 0 }}>Note</label>
            <button type="button" className={'btn sm' + (listening ? ' mic-on' : '')} onClick={() => listening ? stopDictation() : startDictation()}
              title={listening ? 'Stop recording' : 'Dictate your note'} aria-label={listening ? 'Stop recording' : 'Dictate your note'}
              style={listening ? { background: 'var(--critical)', borderColor: 'var(--critical)', color: '#fff' } : undefined}>
              <Icon name="mic" size={15} /> {listening ? 'Recording…' : 'Speak'}
            </button>
          </div>
          <textarea className="textarea" rows={4} autoFocus placeholder="Jot a thought, paste a snippet, or drop a link… or tap Speak to dictate. It'll be filed and turned into study material."
          value={text} onChange={(e) => setText(e.target.value)} style={{ marginTop: 8 }} />
          {listening && <div className="t-small" style={{ display: 'flex', alignItems: 'center', gap: 7, marginTop: 6, color: 'var(--critical)' }}><span className="rec-dot" /> Listening… speak now, your words appear above.</div>}
          {voiceErr && <div className="t-small" style={{ marginTop: 6, color: 'var(--critical)' }}>{voiceErr}</div>}
          <label className="field-label" style={{ marginTop: 16 }}>File under topic</label>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
            {TOPICS.slice(0, 6).map((t) =>
            <button key={t.id} className={'chip' + (topic === t.id ? ' on' : '')} onClick={() => {setTopic(t.id);setAdding(false);}}>{t.name}</button>
            )}
            {adding ?
            <input autoFocus value={custom}
            onChange={(e) => setCustom(e.target.value)}
            onKeyDown={(e) => {if (e.key === 'Enter') {e.preventDefault();if (custom.trim()) {setTopic('new');setAdding(false);}} else if (e.key === 'Escape') {setAdding(false);setCustom('');}}}
            onBlur={() => {if (custom.trim()) setTopic('new');}}
            placeholder="Type a topic…"
            style={{ padding: '6px 12px', borderRadius: 999, border: '1px solid var(--blue)', outline: 'none', background: 'var(--paper)', color: 'var(--ink)', fontSize: 13, fontFamily: 'inherit', minWidth: 150 }} /> :

            <button className={'chip' + (topic === 'new' && custom ? ' on' : '')} onClick={() => setAdding(true)} title="Add a custom topic" style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
              <Icon name="plus" size={13} /> {topic === 'new' && custom ? custom : 'New topic'}
            </button>
            }
          </div>
        </div>
        <div className="mfoot">
          <button className="btn" onClick={() => { stopDictation(); onClose(); }}>Cancel</button>
          <button className="btn primary" disabled={!text.trim()} onClick={() => {
            stopDictation();
            let meta = null;
            if (topic === 'new' && custom.trim()) meta = { topicName: custom.trim(), domain: 'sci' };
            else { const t = TOPICS.find((x) => x.id === topic); if (t) meta = { topicName: t.name, domain: t.domain }; }
            onSave(text, meta);
          }}>
            <Icon name="check" size={16} /> Capture
          </button>
        </div>
      </div>
    </div>);

}

Object.assign(window, { NavWidget, Topbar, MobileTopbar, MobileNav, QuickCapture });