/* ============================================================
   觀察者 v2.1 — Shared shell
   AppShell, Sidebar, Topbar, CandidateSwitcher, BottomNav, MoreSheet
   Pages set window.V21_PAGE = { current: <nav id>, candidate: 'obj'|'kky'|'lwt'|'yjm' }
   then mount <AppShell> around their content.
   ============================================================ */
(function(){
  const { useState, useEffect } = React;

  /* ---------- icons ---------- */
  const Ic = {
    pulse: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" width="16" height="16"><path d="M3 12h4l3-9 4 18 3-9h4"/></svg>,
    eye: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" width="16" height="16"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></svg>,
    person: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" width="16" height="16"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8"/></svg>,
    scales: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" width="16" height="16"><path d="M12 3v18"/><path d="M5 8h14"/><path d="M5 8l-3 6a3 3 0 0 0 6 0z"/><path d="M19 8l-3 6a3 3 0 0 0 6 0z"/><path d="M8 21h8"/></svg>,
    map: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" width="16" height="16"><polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21 3 6"/><line x1="9" y1="3" x2="9" y2="18"/><line x1="15" y1="6" x2="15" y2="21"/></svg>,
    bell: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" strokeLinecap="round" width="16" height="16"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>,
    hash: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" width="16" height="16"><line x1="4" y1="9" x2="20" y2="9"/><line x1="4" y1="15" x2="20" y2="15"/><line x1="10" y1="3" x2="8" y2="21"/><line x1="16" y1="3" x2="14" y2="21"/></svg>,
    layers: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" width="16" height="16"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg>,
    shield: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" width="16" height="16"><path d="M12 2 4 5v7c0 5 3.5 8.5 8 10 4.5-1.5 8-5 8-10V5l-8-3z"/></svg>,
    settings: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" width="16" height="16"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8 2 2 0 1 1-2.8 2.8 1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3 2 2 0 1 1-2.8-2.8 1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8 2 2 0 1 1 2.8-2.8 1.7 1.7 0 0 0 1.8.3 1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3 2 2 0 1 1 2.8 2.8 1.7 1.7 0 0 0-.3 1.8 1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>,
    search: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" width="14" height="14"><circle cx="11" cy="11" r="7"/><line x1="16.5" y1="16.5" x2="21" y2="21"/></svg>,
    filter: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" width="14" height="14"><polygon points="22 3 2 3 10 12.5 10 19 14 21 14 12.5 22 3"/></svg>,
    cal: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" width="14" height="14"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>,
    more: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" width="22" height="22"><circle cx="5" cy="12" r="1.4"/><circle cx="12" cy="12" r="1.4"/><circle cx="19" cy="12" r="1.4"/></svg>,
  };

  /* ---------- Nav config ---------- */
  const NAV = [
    { sec:'OVERVIEW' },
    { id:'objective', ic:Ic.eye, label:'객관 — 3자 구도', href:'index.html', crumb:'OVERVIEW / 객관', title:'전북 도지사 선거 2026 — 통합 대시보드' },

    { sec:'CANDIDATES' },
    { id:'cand_kky', ic:Ic.person, label:'김관영 (현직)', href:'candidate.html?id=kky', crumb:'CANDIDATE / 김관영', title:'김관영 — 후보 모니터링' },
    { id:'cand_lwt', ic:Ic.person, label:'이원택', href:'candidate.html?id=lwt', crumb:'CANDIDATE / 이원택', title:'이원택 — 후보 모니터링' },
    { id:'cand_yjm', ic:Ic.person, label:'양정무', href:'candidate.html?id=yjm', crumb:'CANDIDATE / 양정무', title:'양정무 — 후보 모니터링' },

    { sec:'COMPARE' },
    { id:'policy', ic:Ic.scales, label:'정책 비교', href:'policy.html', crumb:'COMPARE / 정책', title:'정책 입장 매트릭스 — 도민 의견 vs 3자' },
    { id:'regions', ic:Ic.map, label:'14 시군 지도', href:'regions.html', crumb:'COMPARE / 시군', title:'14 시군 — 후보별 점유율' },

    { sec:'MONITOR' },
    { id:'alerts', ic:Ic.bell, label:'알람', href:'alerts.html', crumb:'MONITOR / 알람', title:'알람 — 룰 기반 자동 감지', badge:'5' },
    { id:'keywords', ic:Ic.hash, label:'도민 키워드', href:'keywords.html', crumb:'MONITOR / 키워드', title:'전북 도민 관심 키워드' },
    { id:'channels', ic:Ic.layers, label:'채널 원장', href:'channels.html', crumb:'MONITOR / 채널', title:'채널 원장 — 외부 소스' },

    { sec:'GOVERNANCE' },
    { id:'assets', ic:Ic.shield, label:'자산 인벤토리', href:'assets.html', crumb:'GOV / 자산', title:'자산 인벤토리' },
    { id:'governance', ic:Ic.settings, label:'RAG · 접근로그', href:'governance.html', crumb:'GOV / RAG', title:'RAG · 접근로그' },
  ];
  const NAV_BY_ID = Object.fromEntries(NAV.filter(n=>n.id).map(n=>[n.id,n]));

  // Bottom nav: 4 primary + more
  const BOTTOM_NAV = ['objective','cand_kky','policy','alerts'];

  /* ---------- Candidate switcher (sidebar) ---------- */
  function CandSwitcher({ current }){
    const cands = window.V21.CANDIDATES;
    const navTo = (id) => { window.location.href = `candidate.html?id=${id}`; };
    const navObj = () => { window.location.href = 'index.html'; };
    return (
      <div className="cand-switcher">
        <div className="cand-switcher-label">SUBJECT</div>
        <div className="cand-pill-grid">
          <button
            className={`cand-pill obj${current==='obj'?' active':''}`}
            style={{'--cand-color':'#b8ad95'}}
            onClick={navObj}
          >
            <span className="swatch"/>
            <span className="nm">객관 / 전체</span>
          </button>
          {cands.map(c => (
            <button
              key={c.id}
              className={`cand-pill${current===c.id?' active':''}`}
              style={{'--cand-color':c.color}}
              onClick={()=>navTo(c.id)}
            >
              <span className="swatch"/>
              <span className="nm">{c.name}</span>
              <span className="meta">{c.party.slice(0,3)} · {c.home}</span>
            </button>
          ))}
        </div>
      </div>
    );
  }

  /* ---------- Sidebar ---------- */
  function Sidebar({ currentNav, candidate }){
    return (
      <aside className="sidebar">
        <div className="sidebar-brand">
          <div className="logo">
            <span className="han">觀察者</span>
            <span className="v">v2.1</span>
          </div>
          <div className="tag">JEONBUK GOVERNOR · 2026</div>
        </div>
        <CandSwitcher current={candidate}/>
        <nav className="sidebar-nav">
          {NAV.map((it,i) => it.sec
            ? <div key={'sec-'+i} className="sidebar-section">{it.sec}</div>
            : <a key={it.id} className={`sidebar-link${currentNav===it.id?' active':''}`} href={it.href}>
                <span className="ico">{it.ic}</span>
                <span>{it.label}</span>
                {it.badge && <span className="badge">{it.badge}</span>}
              </a>
          )}
        </nav>
        <div className="sidebar-foot">
          <div className="row"><span>BUILD</span><span className="v">2026.05.01</span></div>
          <div className="row"><span>DATA</span><span className="v">D-218</span></div>
          <div className="row"><span>SUBJECTS</span><span className="v">3+1</span></div>
        </div>
      </aside>
    );
  }

  /* ---------- Topbar ---------- */
  function Topbar({ currentNav, candidate }){
    const meta = NAV_BY_ID[currentNav] || NAV_BY_ID.objective;
    const c = candidate && candidate!=='obj' ? window.V21.C_BY_ID[candidate] : null;
    return (
      <header className="topbar">
        <div className="topbar-mobile-brand">
          <span className="han">觀察者</span>
          <span className="v">v2.1</span>
        </div>
        <div className="topbar-title">
          <div className="crumb">
            <span>{meta.crumb}</span>
            {c && <span className="accent-tag">{c.name}</span>}
          </div>
          <h1>{meta.title}</h1>
        </div>
        <div className="topbar-spacer"/>
        <div className="topbar-search">
          <span style={{display:'inline-flex'}}>{Ic.search}</span>
          <input placeholder="후보, 이슈, 시군, 자산..." defaultValue=""/>
          <span className="mono kbd">⌘K</span>
        </div>
        <button className="topbar-btn" title="필터">{Ic.filter}</button>
        <button className="topbar-btn" title="달력">{Ic.cal}</button>
        <button className="topbar-btn" title="알람" onClick={()=>{window.location.href='alerts.html';}}>
          {Ic.bell}<span className="dot"></span>
        </button>

        {/* Mobile: candidate switcher in topbar */}
        <div className="topbar-cand-switcher">
          <button className={`tcs${candidate==='obj'?' active':''}`} style={{'--cand-color':'#6b5d4a'}} onClick={()=>window.location.href='index.html'} title="객관">전</button>
          <button className={`tcs${candidate==='kky'?' active':''}`} style={{'--cand-color':'#4ba3d4'}} onClick={()=>window.location.href='candidate.html?id=kky'} title="김관영">관</button>
          <button className={`tcs${candidate==='lwt'?' active':''}`} style={{'--cand-color':'#1f3a68'}} onClick={()=>window.location.href='candidate.html?id=lwt'} title="이원택">원</button>
          <button className={`tcs${candidate==='yjm'?' active':''}`} style={{'--cand-color':'#c8362a'}} onClick={()=>window.location.href='candidate.html?id=yjm'} title="양정무">정</button>
        </div>

        <div className="topbar-user">
          <div className="avatar">데</div>
          <div className="topbar-user-meta">
            <div className="name">데이터팀</div>
            <div className="role">DATA · LEAD</div>
          </div>
        </div>
      </header>
    );
  }

  /* ---------- Bottom nav (mobile) ---------- */
  function BottomNav({ currentNav, onMore, moreOpen }){
    return (
      <nav className="bottom-nav">
        {BOTTOM_NAV.map(id => {
          const it = NAV_BY_ID[id];
          return (
            <a key={id} className={`bn-item${currentNav===id?' active':''}`} href={it.href}>
              <span className="ic">{it.ic}</span>
              <span className="lbl">{it.label.split(' ')[0]}</span>
              {it.badge && <span className="badge">{it.badge}</span>}
            </a>
          );
        })}
        <button className={`bn-item${moreOpen?' active':''}`} onClick={onMore}>
          <span className="ic">{Ic.more}</span>
          <span className="lbl">더보기</span>
        </button>
      </nav>
    );
  }

  /* ---------- More sheet ---------- */
  function MoreSheet({ open, onClose, currentNav }){
    return (
      <div className={`more-sheet${open?' open':''}`}>
        <div className="more-sheet-bd" onClick={onClose}/>
        <div className="more-sheet-panel">
          <div className="more-sheet-handle"/>
          <div className="more-sheet-h">
            <h3>전체 메뉴</h3>
            <span className="crumb">v2.1 · NAVIGATION</span>
          </div>
          <div style={{display:'flex', flexDirection:'column', gap:14}}>
            {NAV.filter(n=>n.id).reduce((acc,it)=>{
              const sec = NAV.slice(0, NAV.indexOf(it)).reverse().find(n=>n.sec)?.sec || '';
              if(!acc[sec]) acc[sec] = [];
              acc[sec].push(it);
              return acc;
            }, {}) && Object.entries(NAV.filter(n=>n.id).reduce((acc,it)=>{
              const sec = NAV.slice(0, NAV.indexOf(it)).reverse().find(n=>n.sec)?.sec || 'OTHER';
              if(!acc[sec]) acc[sec] = [];
              acc[sec].push(it);
              return acc;
            }, {})).map(([sec, items]) => (
              <div key={sec}>
                <div style={{fontFamily:'IBM Plex Mono', fontSize:9.5, letterSpacing:'.16em', color:'var(--ink-3)', padding:'4px 4px 6px'}}>{sec}</div>
                <div className="more-grid">
                  {items.map(it => (
                    <a key={it.id} className={`more-item${currentNav===it.id?' active':''}`} href={it.href}>
                      <span className="ic">{it.ic}</span>
                      <span>{it.label}</span>
                      {it.badge && <span className="badge">{it.badge}</span>}
                    </a>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  /* ---------- AppShell ---------- */
  function AppShell({ currentNav='objective', candidate='obj', children }){
    const [moreOpen, setMoreOpen] = useState(false);
    useEffect(()=>{
      // set candidate attr on root for accent vars
      document.documentElement.setAttribute('data-candidate', candidate);
    }, [candidate]);
    return (
      <div className="app-shell">
        <Sidebar currentNav={currentNav} candidate={candidate}/>
        <div className="main-area">
          <Topbar currentNav={currentNav} candidate={candidate}/>
          <main className="content">{children}</main>
        </div>
        <BottomNav currentNav={currentNav} onMore={()=>setMoreOpen(true)} moreOpen={moreOpen}/>
        <MoreSheet open={moreOpen} onClose={()=>setMoreOpen(false)} currentNav={currentNav}/>
      </div>
    );
  }

  /* ---------- Page strip — eyebrow above each page ---------- */
  function PageStrip({ icon, eyebrow, title, meta, stats }){
    return (
      <div className="page-strip">
        {icon && <div className="strip-icon">{icon}</div>}
        <div className="strip-body">
          <div className="strip-eyebrow">{eyebrow}</div>
          <div className="strip-title">{title}</div>
          {meta && <div className="strip-meta">{meta}</div>}
        </div>
        {stats && (
          <div className="strip-stats">
            {stats.map((s,i) => (
              <div key={i} className="stat">
                <div className="lbl">{s.lbl}</div>
                <div className="v">{s.v}</div>
                {s.delta!=null && (
                  <div className={`delta ${s.delta>0?'up':s.delta<0?'down':''}`}>
                    {s.delta>0?'▲':s.delta<0?'▼':'·'} {Math.abs(s.delta).toFixed(1)}
                  </div>
                )}
              </div>
            ))}
          </div>
        )}
      </div>
    );
  }

  /* ---------- Card primitive ---------- */
  function Card({ title, eyebrow, right, children, dark, tinted, flush, style, className }){
    return (
      <div className={`card${dark?' dark':''}${tinted?' tinted':''}${flush?' flush':''}${className?' '+className:''}`} style={style}>
        {(title || eyebrow || right) && (
          <div className="card-h">
            <div>
              {eyebrow && <div className="eye">{eyebrow}</div>}
              {title && <div className="t">{title}</div>}
            </div>
            {right && <div className="card-h-r">{right}</div>}
          </div>
        )}
        {children}
      </div>
    );
  }

  /* ---------- expose ---------- */
  window.V21_SHELL = { AppShell, PageStrip, Card, Ic, NAV, NAV_BY_ID };
})();
