/* shell.jsx — shared chrome for the live 咔嗒Lab site.
   Exports: KataPage, KataNav, KataFooter, KataTweaks, Backdrop, Reveal,
            Avatar, useKataTheme, plus small icons.
   Requires: components.jsx (KSITE, Logo, ThemeToggle, Arrow), tweaks-panel.jsx */

const SOFT_MAP_S = { '柔和': 0.62, '适中': 1.0, '立体': 1.5 };
const ACCENTS_S = ['#2f6df0', '#1f9cf0', '#0fb5c4', '#6366f1'];

/* ---- icons ---- */
const IcSearch = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" {...p}><circle cx="11" cy="11" r="7" /><path d="M21 21l-4-4" /></svg>;
const IcHeart = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 20s-7-4.6-9.3-9C1 7.7 2.6 4.8 5.8 4.8c2 0 3.2 1.3 4.2 2.6 1-1.3 2.2-2.6 4.2-2.6 3.2 0 4.8 2.9 3.1 6.2C19 15.4 12 20 12 20z" /></svg>;
const IcChat = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M21 12a8 8 0 0 1-8 8H4l2-3a8 8 0 1 1 15-5z" /></svg>;
const IcPlay = (p) => <svg viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M8 5.5v13l11-6.5z" /></svg>;
const IcStar = (p) => <svg viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M12 3l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 17.8 6.8 19.2l1-5.8L3.5 9.2l5.9-.9z" /></svg>;
const IcUp = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M5 12l7-7 7 7M12 5v14" /></svg>;
const IcMenu = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" {...p}><path d="M4 7h16M4 12h16M4 17h16" /></svg>;
const IcEye = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z" /><circle cx="12" cy="12" r="3" /></svg>;
Object.assign(window, { IcSearch, IcHeart, IcChat, IcPlay, IcStar, IcUp, IcMenu, IcEye });

/* ---- theme ---- */
function useKataTheme() {
  const read = () => { try { return JSON.parse(localStorage.getItem('kata.theme') || 'null'); } catch { return null; } };
  const init = Object.assign({ dark: false, accent: '#2f6df0', soft: '适中', intro: '关键词高亮', titleAnim: '拼合' }, read() || {});
  const [t, setTweak] = useTweaks(init);
  const soft = SOFT_MAP_S[t.soft] ?? 1;
  React.useEffect(() => {
    const r = document.documentElement;
    r.setAttribute('data-theme', t.dark ? 'dark' : 'light');
    r.style.setProperty('--accent', t.accent);
    r.style.setProperty('--soft', String(soft));
    document.body.classList.add('ka-body');
    try { localStorage.setItem('kata.theme', JSON.stringify({ dark: t.dark, accent: t.accent, soft: t.soft, intro: t.intro, titleAnim: t.titleAnim })); } catch {}
  }, [t.dark, t.accent, soft, t.intro, t.titleAnim]);
  return [t, setTweak];
}

/* theme context so any page can read tweak values */
const KataCtx = React.createContext({ t: {}, setTweak: () => {} });
function useKata() { return React.useContext(KataCtx); }

/* ---- nav ---- */
function AccountMenu() {
  const db = useKataDB();
  const me = db.currentUser();
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    if (!open) return;
    const close = () => setOpen(false);
    window.addEventListener('click', close);
    return () => window.removeEventListener('click', close);
  }, [open]);
  if (!me) {
    return <a href="#auth" className="btn btn-accent" style={{ padding: '12px 24px', fontSize: 14.5 }}>加入社区</a>;
  }
  return (
    <div className="ka-acct" style={{ position: 'relative' }} onClick={(e) => e.stopPropagation()}>
      <button className="ka-acct-btn theme-toggle ka-iconbtn" onClick={() => setOpen((o) => !o)} aria-label="account" style={{ width: 'auto', padding: '6px 12px 6px 6px', gap: 9, display: 'flex', alignItems: 'center' }}>
        <Avatar name={me.name} hue={me.hue} size={30} />
        <span style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink)', maxWidth: 96, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{me.name}</span>
      </button>
      {open && (
        <div className="nm-rise ka-acct-pop" style={{ position: 'absolute', right: 0, top: 'calc(100% + 10px)', minWidth: 220, borderRadius: 16, padding: 14, zIndex: 60 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '4px 6px 12px' }}>
            <Avatar name={me.name} hue={me.hue} size={40} />
            <span style={{ minWidth: 0 }}>
              <div style={{ fontSize: 14.5, fontWeight: 600, color: 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{me.name}</div>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--ink-faint)' }}>{me.handle}</div>
            </span>
          </div>
          <div style={{ height: 1, background: 'var(--hair)', margin: '0 2px 8px' }} />
          <a href="#post" style={{ display: 'block', padding: '10px 8px', borderRadius: 10, fontSize: 14, color: 'var(--ink-soft)', fontWeight: 500 }}>我的社区</a>
          <a href="#new_post" style={{ display: 'block', padding: '10px 8px', borderRadius: 10, fontSize: 14, color: 'var(--ink-soft)', fontWeight: 500 }}>发布帖子</a>
          <button onClick={() => { db.logout(); setOpen(false); }} style={{ width: '100%', textAlign: 'left', padding: '10px 8px', borderRadius: 10, fontSize: 14, color: 'var(--accent)', fontWeight: 600, background: 'none', border: 'none', cursor: 'pointer' }}>退出登录</button>
        </div>
      )}
    </div>
  );
}

function KataNav({ active, dark, onToggle }) {
  const [open, setOpen] = React.useState(false);
  return (
    <div className="ka-navwrap">
      <div className="kc">
        <nav className="ka-nav">
          <a href="#home" className="ka-brand" aria-label="咔嗒Lab"><Logo height={34} /></a>
          <div className="links">
            {KSITE.nav.map((n) => (
              <a key={n.key} href={n.href} className={active === n.key ? 'on' : ''}>{n.label}</a>
            ))}
          </div>
          <div className="spacer" />
          <div className="right">
            <ThemeToggle dark={dark} onToggle={onToggle} className="theme-toggle ka-iconbtn" />
            <AccountMenu />
            <button className="theme-toggle ka-iconbtn ka-menu-btn" onClick={() => setOpen((o) => !o)} aria-label="menu"><IcMenu style={{ width: 19, height: 19 }} /></button>
          </div>
        </nav>
        {open && (
          <div className="nm-rise" style={{ marginTop: 10, borderRadius: 18, padding: 12, display: 'flex', flexDirection: 'column', gap: 4 }}>
            {KSITE.nav.map((n) => (
              <a key={n.key} href={n.href} style={{ padding: '12px 16px', borderRadius: 12, fontWeight: 500, color: active === n.key ? 'var(--accent)' : 'var(--ink-soft)' }}>{n.label}</a>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function KataFooter() {
  return (
    <footer className="ka-foot">
      <div className="kc in">
        <div className="l"><Logo height={22} /><span className="cr">© 2026 咔嗒Lab · Kata Lab</span></div>
        <div className="links">
          <a href="#community">社区活动</a>
          <a href="#enterprise">企业活动</a>
          <a href="#demo">技术广场</a>
          <a href="#post">社区发帖</a>
          <a href="#home">关于我们</a>
        </div>
      </div>
    </footer>
  );
}

function KataTweaks({ t, setTweak }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="外观" />
      <TweakToggle label="深色模式" value={t.dark} onChange={(v) => setTweak('dark', v)} />
      <TweakColor label="点缀色" value={t.accent} options={ACCENTS_S} onChange={(v) => setTweak('accent', v)} />
      <TweakRadio label="立体感" value={t.soft} options={['柔和', '适中', '立体']} onChange={(v) => setTweak('soft', v)} />
    </TweaksPanel>
  );
}

/* ---- animated backdrop (orbs + dot grid + particles + parallax) ---- */
function Backdrop({ particles = 6 }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    let raf = 0, tx = 0, ty = 0, cx = 0, cy = 0;
    const onMove = (e) => {
      const w = window.innerWidth, h = window.innerHeight;
      tx = (e.clientX / w - 0.5) * 2; ty = (e.clientY / h - 0.5) * 2;
      if (!raf) raf = requestAnimationFrame(tick);
    };
    const tick = () => {
      cx += (tx - cx) * 0.06; cy += (ty - cy) * 0.06;
      el.style.setProperty('--mx', cx.toFixed(3));
      el.style.setProperty('--my', cy.toFixed(3));
      raf = (Math.abs(tx - cx) > 0.002 || Math.abs(ty - cy) > 0.002) ? requestAnimationFrame(tick) : 0;
    };
    window.addEventListener('pointermove', onMove, { passive: true });
    return () => { window.removeEventListener('pointermove', onMove); cancelAnimationFrame(raf); };
  }, []);
  const pts = Array.from({ length: particles }, (_, i) => {
    const left = 8 + (i * 83) % 84;
    const dur = 11 + (i % 4) * 3.5;
    const delay = -(i * 2.3);
    const size = 3 + (i % 3) * 1.5;
    return <span key={i} className="pt" style={{ left: left + '%', bottom: '8%', width: size, height: size, animationDuration: dur + 's', animationDelay: delay + 's' }} />;
  });
  return (
    <div className="backdrop" ref={ref} aria-hidden="true">
      <span className="grid" />
      <span className="orb orb-1" />
      <span className="orb orb-2" />
      <span className="orb orb-3" />
      {pts}
    </div>
  );
}


/* ---- sitewide tech constellation network (all pages)
   Match Hero v16: sparse floating dots + soft ambient links,
   with stronger random-looking connections near the cursor. ---- */
function TechNetBG() {
  const wrapRef = React.useRef(null);
  const cvRef = React.useRef(null);
  React.useEffect(() => {
    const wrap = wrapRef.current, cv = cvRef.current;
    if (!wrap || !cv) return;
    const ctx = cv.getContext('2d');
    let W = 0, H = 0, dpr = 1, pts = [], raf = 0, running = true;
    let colors = ['#2f6df0', '#8b5cf6', '#0ea5e9'];
    const mouse = { x: -9999, y: -9999 };
    /* Hero-like sparse field (NOT dense lattice) */
    const N_BASE = 78, LINK = 118, MLINK = 210;

    function readColors() {
      const ACC = getComputedStyle(document.documentElement).getPropertyValue('--accent').trim() || '#2f6df0';
      const dark = document.documentElement.getAttribute('data-theme') === 'dark';
      colors = dark
        ? [ACC, '#a78bfa', '#38bdf8']
        : [ACC, '#8b5cf6', '#0ea5e9'];
      return colors;
    }
    function resize() {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      W = window.innerWidth; H = window.innerHeight;
      cv.width = Math.max(1, Math.floor(W * dpr));
      cv.height = Math.max(1, Math.floor(H * dpr));
      cv.style.width = W + 'px'; cv.style.height = H + 'px';
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      const palette = readColors();
      /* keep density similar across viewports, but never dense */
      const dens = Math.min(W / 1280, 1.15) * Math.min(H / 900, 1.05);
      const n = Math.max(48, Math.round(N_BASE * dens));
      pts = Array.from({ length: n }, () => ({
        x: Math.random() * W, y: Math.random() * H,
        vx: (Math.random() - .5) * .26, vy: (Math.random() - .5) * .26,
        r: 1.15 + Math.random() * 1.55,
        c: palette[Math.random() * 3 | 0],
        tw: Math.random() * Math.PI * 2
      }));
    }
    resize();
    const onResize = () => resize();
    window.addEventListener('resize', onResize);

    const mo = new MutationObserver(() => {
      const palette = readColors();
      pts.forEach((p) => { p.c = palette[Math.random() * 3 | 0]; });
    });
    mo.observe(document.documentElement, { attributes: true, attributeFilter: ['style', 'data-theme'] });

    const onMove = (e) => { mouse.x = e.clientX; mouse.y = e.clientY; };
    const onLeave = () => { mouse.x = mouse.y = -9999; };
    window.addEventListener('pointermove', onMove, { passive: true });
    document.addEventListener('mouseleave', onLeave);

    const darkNow = () => document.documentElement.getAttribute('data-theme') === 'dark';

    function drawStatic() {
      ctx.clearRect(0, 0, W, H);
      /* very soft ambient dots only; links appear with cursor motion */
      for (const p of pts) {
        ctx.globalAlpha = darkNow() ? .28 : .18;
        ctx.fillStyle = p.c;
        ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, 7); ctx.fill();
      }
      ctx.globalAlpha = 1;
    }

    function frame(t) {
      if (!running) return;
      ctx.clearRect(0, 0, W, H);
      const isDark = darkNow();
      /* ambient baseline is intentionally faint; cursor creates random-looking links */
      const baseLink = isDark ? .16 : .11;
      const nodeA0 = isDark ? .30 : .22;
      const nodeA1 = isDark ? .22 : .18;

      for (let i = 0; i < pts.length; i++) {
        const p = pts[i];
        p.x += p.vx; p.y += p.vy;
        if (p.x < -10) p.x = W + 10; if (p.x > W + 10) p.x = -10;
        if (p.y < -10) p.y = H + 10; if (p.y > H + 10) p.y = -10;

        /* soft twinkle dots */
        const a = nodeA0 + nodeA1 * (0.5 + 0.5 * Math.sin(t / 900 + p.tw));
        ctx.globalAlpha = a * .22; ctx.fillStyle = p.c;
        ctx.beginPath(); ctx.arc(p.x, p.y, p.r * 3.1, 0, 7); ctx.fill();
        ctx.globalAlpha = a; ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, 7); ctx.fill();

        for (let k = i + 1; k < pts.length; k++) {
          const q = pts[k];
          const d = Math.hypot(p.x - q.x, p.y - q.y);
          if (d < LINK) {
            const mx = (p.x + q.x) / 2, my = (p.y + q.y) / 2;
            const near = Math.hypot(mx - mouse.x, my - mouse.y);
            /* only a soft ambient web + stronger near-cursor links */
            const boost = near < MLINK + LINK ? (1 - near / (MLINK + LINK)) * .52 : 0;
            const alpha = (1 - d / LINK) * (baseLink + boost);
            if (alpha < 0.02) continue;
            ctx.globalAlpha = alpha;
            ctx.strokeStyle = p.c; ctx.lineWidth = 1.0 + boost * .55;
            ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(q.x, q.y); ctx.stroke();
          }
        }
        /* particle ↔ cursor spokes (the "random lines follow cursor" feel) */
        const dm = Math.hypot(p.x - mouse.x, p.y - mouse.y);
        if (dm < MLINK) {
          ctx.globalAlpha = (1 - dm / MLINK) * (isDark ? .58 : .48);
          ctx.strokeStyle = p.c; ctx.lineWidth = 1.15;
          ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(mouse.x, mouse.y); ctx.stroke();
        }
      }
      ctx.globalAlpha = 1;
      raf = requestAnimationFrame(frame);
    }

    if (matchMedia('(prefers-reduced-motion: reduce)').matches) {
      drawStatic();
    } else {
      frame(performance.now());
    }

    return () => {
      running = false;
      cancelAnimationFrame(raf);
      mo.disconnect();
      window.removeEventListener('resize', onResize);
      window.removeEventListener('pointermove', onMove);
      document.removeEventListener('mouseleave', onLeave);
    };
  }, []);

  return (
    <div className="ka-technet" ref={wrapRef} aria-hidden="true">
      <canvas ref={cvRef} className="ka-technet-cv" />
    </div>
  );
}

/* ---- sitewide aurora atmosphere blobs ---- */
function AuroraBG() {
  return (
    <div className="ka-aurora" aria-hidden="true">
      <i className="ka-blob ka-blob-a" />
      <i className="ka-blob ka-blob-b" />
      <i className="ka-blob ka-blob-c" />
      <i className="ka-mesh" />
      <i className="ka-grain" />
    </div>
  );
}

/* ---- reveal on scroll ---- */
function Reveal({ children, delay = 0, className = '', tag = 'div', style = {} }) {
  const ref = React.useRef(null);
  const [seen, setSeen] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => { if (e.isIntersecting) { setSeen(true); io.disconnect(); } });
    }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
    io.observe(el); return () => io.disconnect();
  }, []);
  const T = tag;
  return <T ref={ref} className={`reveal ${seen ? 'in' : ''} ${className}`} style={{ transitionDelay: (delay) + 'ms', ...style }}>{children}</T>;
}

function Avatar({ name, hue = 222, size = 44, className = 'ava-c' }) {
  const ch = (name || '?').trim().charAt(0).toUpperCase();
  return <span className={className} style={{ width: size, height: size, background: `linear-gradient(150deg, hsl(${hue} 58% 58%), hsl(${hue + 24} 56% 48%))` }}>{ch}</span>;
}

/* ---- page wrapper ---- */
function KataPage({ active, children, atmos = true }) {
  const [t, setTweak] = useKataTheme();
  const kids = React.Children.map(children, (ch) => React.isValidElement(ch) ? React.cloneElement(ch, { kt: t }) : ch);
  return (
    <KataCtx.Provider value={{ t, setTweak }}>
      <div className="ka" data-page={active || 'none'}>
        {atmos && <AuroraBG />}
        {atmos && <TechNetBG />}
        <KataNav active={active} dark={t.dark} onToggle={() => setTweak('dark', !t.dark)} />
        <div className="ka-content">
          {kids}
        </div>
        <KataFooter />
        <KataTweaks t={t} setTweak={setTweak} />
      </div>
    </KataCtx.Provider>
  );
}

Object.assign(window, { useKataTheme, useKata, KataCtx, KataNav, KataFooter, KataTweaks, Backdrop, Reveal, Avatar, KataPage, TechNetBG, AuroraBG });
