/* AI Edge — structural parts. Requires React (global) + window.AE. */
const { useState, useEffect, useRef } = React;
const AE = window.AE;

/* ---------------- icons ---------------- */
const I = {
  glyph:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round"/><path d="M12 7v10M7.5 9.5 12 12l4.5-2.5" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round"/></svg>,
  play:(p={})=> <svg viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M8 5v14l11-7L8 5Z"/></svg>,
  copy:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><rect x="9" y="9" width="11" height="11" rx="2.5" stroke="currentColor" strokeWidth="1.6"/><path d="M5 15V6a2 2 0 0 1 2-2h8" stroke="currentColor" strokeWidth="1.6"/></svg>,
  check:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="m5 12.5 4.5 4.5L19 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  aDown:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M12 5v14m0 0 6-6m-6 6-6-6" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  aRight:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M5 12h14m0 0-6-6m6 6-6 6" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  retrieval:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><circle cx="11" cy="11" r="6" stroke="currentColor" strokeWidth="1.7"/><path d="m20 20-4-4" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/><path d="M8.5 11h5M11 8.5v5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>,
  agent:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><rect x="4" y="8" width="16" height="11" rx="2.5" stroke="currentColor" strokeWidth="1.7"/><path d="M12 4v4M8 13h.01M16 13h.01M9 16h6" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/></svg>,
  sql:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><ellipse cx="12" cy="6" rx="7" ry="3" stroke="currentColor" strokeWidth="1.7"/><path d="M5 6v6c0 1.66 3.13 3 7 3s7-1.34 7-3V6M5 12v6c0 1.66 3.13 3 7 3s7-1.34 7-3v-6" stroke="currentColor" strokeWidth="1.7"/></svg>,
  eval:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M4 20V4M4 20h16" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/><path d="M8 16v-4M12 16V8M16 16v-6" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round"/></svg>,
  shield:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M12 3 5 6v5c0 4.5 3 7.5 7 9 4-1.5 7-4.5 7-9V6l-7-3Z" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round"/><path d="m9 12 2 2 4-4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  ban:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><circle cx="12" cy="12" r="8" stroke="currentColor" strokeWidth="1.6"/><path d="m7 7 10 10" stroke="currentColor" strokeWidth="1.6"/></svg>,
  mail:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><rect x="3" y="5" width="18" height="14" rx="2.5" stroke="currentColor" strokeWidth="1.6"/><path d="m4 7 8 6 8-6" stroke="currentColor" strokeWidth="1.6"/></svg>,
  download:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M12 4v10m0 0 4-4m-4 4-4-4M5 18h14" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  ext:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M14 5h5v5M19 5l-8 8M18 14v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  warn:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M12 4 2.5 20h19L12 4Z" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round"/><path d="M12 10v4M12 17h.01" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/></svg>,
  user:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><circle cx="12" cy="8" r="3.5" stroke="currentColor" strokeWidth="1.6"/><path d="M5 20c0-3.3 3.1-6 7-6s7 2.7 7 6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>,
  refresh:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M20 11a8 8 0 1 0-.5 4M20 5v6h-6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  spark:(p={})=> <svg viewBox="0 0 24 24" fill="none" {...p}><path d="M12 3v18M3 12h18M6 6l12 12M18 6 6 18" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/></svg>,
};
const capIcon = { retrieval:I.retrieval, agent:I.agent, sql:I.sql, eval:I.eval };
const trustIcon = { npm:I.download, git:I.ext, chart:I.eval };

/* ---------------- hooks ---------------- */
function useInView(opts){
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(()=>{
    const el = ref.current; if(!el) return;
    const io = new IntersectionObserver((es)=>{
      es.forEach(e=>{ if(e.isIntersecting){ setSeen(true); io.disconnect(); } });
    }, opts || { threshold:0.2, rootMargin:"0px 0px -8% 0px" });
    io.observe(el); return ()=>io.disconnect();
  },[]);
  return [ref, seen];
}
function Reveal({ children, as="div", delay=0, className="", ...rest }){
  const [ref, seen] = useInView();
  const Tag = as;
  return <Tag ref={ref} className={`rv ${seen?"in":""} ${className}`} style={{ transitionDelay:(delay)+"ms" }} {...rest}>{children}</Tag>;
}
function CountUp({ to, dec=0, suf="", dur=1300 }){
  const [ref, seen] = useInView({ threshold:0.5 });
  const [val, setVal] = useState(0);
  useEffect(()=>{
    if(!seen) return;
    if(document.documentElement.dataset.motion==="off"){ setVal(to); return; }
    let raf, t0;
    const step=(t)=>{ if(!t0)t0=t; const p=Math.min(1,(t-t0)/dur); const e=1-Math.pow(1-p,3);
      setVal(to*e); if(p<1) raf=requestAnimationFrame(step); };
    raf=requestAnimationFrame(step); return ()=>cancelAnimationFrame(raf);
  },[seen,to]);
  const shown = dec>0 ? val.toFixed(dec) : Math.round(val).toLocaleString();
  return <span ref={ref}>{shown}<span className="u">{suf}</span></span>;
}

/* ---------------- Nav ---------------- */
function Nav(){
  const [sc,setSc]=useState(false);
  useEffect(()=>{ const f=()=>setSc(window.scrollY>12); f(); window.addEventListener("scroll",f,{passive:true}); return ()=>window.removeEventListener("scroll",f); },[]);
  return (
    <nav className={`nav ${sc?"scrolled":""}`}>
      <div className="wrap nav-inner">
        <a href="#top" className="brand"><span className="glyph accent"><I.glyph/></span>{AE.brand}</a>
        <div className="nav-links">
          <a href="#video">{AE.ui.navDemo}</a><a href="#caps">{AE.ui.navCaps}</a><a href="#demo">{AE.ui.navQA}</a><a href="#sql">{AE.ui.navSQL}</a><a href="#open">{AE.ui.navOpen}</a>
        </div>
        <div className="nav-spacer"></div>
        <div className="nav-end">
          <div className="lang-seg" role="group" aria-label="language">
            <button className={window.__lang!=="en"?"on":""} onClick={()=>{ if(window.__lang==="en"&&window.__toggleLang) window.__toggleLang(); }}>中</button>
            <button className={window.__lang==="en"?"on":""} onClick={()=>{ if(window.__lang!=="en"&&window.__toggleLang) window.__toggleLang(); }}>EN</button>
          </div>
          <a href={AE.resume} download="郭琦-AI应用-简历.pdf" className="btn btn-ghost btn-sm"><I.download/>{AE.ui.resumeShort}</a>
        </div>
      </div>
    </nav>
  );
}

/* ---------------- Hero canvas ---------------- */
function HeroCanvas(){
  const cv = useRef(null);
  useEffect(()=>{
    const canvas = cv.current, ctx = canvas.getContext("2d");
    let w,h,dpr,parts=[],raf,frame=0,accent="oklch(0.8 0.15 158)";
    const root = document.documentElement;
    function readAccent(){ const c=getComputedStyle(root).getPropertyValue("--accent").trim(); if(c) accent=c; }
    function size(){
      dpr=Math.min(2,window.devicePixelRatio||1);
      const r=canvas.parentElement.getBoundingClientRect();
      w=r.width; h=r.height; canvas.width=w*dpr; canvas.height=h*dpr;
      ctx.setTransform(dpr,0,0,dpr,0,0);
      const gap=46, cols=Math.ceil(w/gap)+1, rows=Math.ceil(h/gap)+1;
      parts=[]; for(let y=0;y<rows;y++)for(let x=0;x<cols;x++){
        parts.push({ gx:x*gap, gy:y*gap, x:x*gap, y:y*gap,
          vx:(Math.random()-.5)*0.25, vy:(Math.random()-.5)*0.25, ph:Math.random()*Math.PI*2 });
      }
    }
    function draw(){
      frame++; if(frame%40===0) readAccent();
      const mode=root.dataset.heroFx||"grid", motion=root.dataset.motion!=="off";
      ctx.clearRect(0,0,w,h);
      const t=frame*0.012;
      const fx=w*(0.5+0.32*Math.sin(t*0.6)), fy=h*(0.4+0.3*Math.cos(t*0.5));
      const scanY = ((frame*1.6)%(h+160))-80;
      if(mode==="constellation"){
        for(const p of parts){ if(motion){ p.x+=p.vx; p.y+=p.vy;
          if(p.x<0||p.x>w)p.vx*=-1; if(p.y<0||p.y>h)p.vy*=-1; } }
        ctx.lineWidth=1;
        for(let i=0;i<parts.length;i+=2){ const a=parts[i];
          for(let j=i+2;j<parts.length;j+=2){ const b=parts[j];
            const d=Math.hypot(a.x-b.x,a.y-b.y);
            if(d<74){ ctx.strokeStyle=accent; ctx.globalAlpha=(1-d/74)*0.14;
              ctx.beginPath(); ctx.moveTo(a.x,a.y); ctx.lineTo(b.x,b.y); ctx.stroke(); } } }
        ctx.globalAlpha=1;
        for(let i=0;i<parts.length;i+=2){ const p=parts[i];
          ctx.fillStyle=accent; ctx.globalAlpha=0.5; ctx.beginPath(); ctx.arc(p.x,p.y,1.4,0,7); ctx.fill(); }
        ctx.globalAlpha=1; return;
      }
      // grid / scan / off : dot grid
      for(const p of parts){
        const dx=p.gx-fx, dy=p.gy-fy, dist=Math.hypot(dx,dy);
        let glow = Math.max(0, 1 - dist/300);
        if(motion && mode==="grid") glow *= (0.7+0.3*Math.sin(t*2+p.ph));
        if(mode==="scan"){ const ds=Math.abs(p.gy-scanY); glow = Math.max(glow*0.25, motion? Math.max(0,1-ds/60):0); }
        if(mode==="off") glow = Math.max(0, 1-dist/360)*0.5;
        const base=0.16;
        ctx.beginPath(); ctx.arc(p.gx,p.gy,1.3+glow*1.4,0,7);
        ctx.fillStyle=accent; ctx.globalAlpha=base+glow*0.65; ctx.fill();
      }
      ctx.globalAlpha=1;
      if(mode==="scan" && motion){
        ctx.strokeStyle=accent; ctx.globalAlpha=0.10; ctx.lineWidth=2;
        ctx.beginPath(); ctx.moveTo(0,scanY); ctx.lineTo(w,scanY); ctx.stroke(); ctx.globalAlpha=1;
      }
    }
    function loop(){ draw(); raf=requestAnimationFrame(loop); }
    readAccent(); size(); loop();
    const ro=new ResizeObserver(size); ro.observe(canvas.parentElement);
    return ()=>{ cancelAnimationFrame(raf); ro.disconnect(); };
  },[]);
  return <canvas ref={cv} className="hero-canvas"></canvas>;
}

/* ---------------- Hero ---------------- */
function Hero(){
  const [copied,setCopied]=useState(false);
  const cmd="npm i "+AE.npm;
  const copy=()=>{ navigator.clipboard?.writeText(cmd); setCopied(true); setTimeout(()=>setCopied(false),1400); };
  return (
    <header className="hero" id="top">
      <HeroCanvas/>
      <div className="hero-fade"></div>
      <div className="wrap hero-inner">
        <div className="eyebrow rv in">{AE.hero.eyebrow}</div>
        <h1 className="rv in">AI <span className="mk">Edge</span></h1>
        <p className="hero-tag rv in" style={{transitionDelay:"60ms"}}>
          <b>{AE.hero.tag[0]}</b><br/><span className="hero-sub" style={{color:"var(--muted)"}}>{AE.hero.tag[1]}</span>
        </p>
        <div className="hero-cta rv in" style={{transitionDelay:"120ms"}}>
          <a href="#video" className="btn btn-primary"><I.play/>看 3 分钟演示</a>
          <div className="term">
            <span className="prompt">$</span><code>{cmd}</code>
            <button className="copy" onClick={copy} aria-label="复制">{copied? <I.check/> : <I.copy/>}</button>
          </div>
        </div>
        <div className="hero-meta rv in" style={{transitionDelay:"180ms"}}>
          <span className="chip"><span className="dot"></span>Hit Rate@5 ≥ 90%</span>
          <span className="chip">MIT 开源</span>
          <span className="chip">P95 ~412ms</span>
          <span className="chip">中文优化</span>
        </div>
      </div>
    </header>
  );
}

/* ---------------- Capabilities + Pipeline ---------------- */
function Capabilities(){
  return (
    <section className="section-pad" id="caps">
      <div className="wrap">
        <Reveal className="section-head">
          <div className="eyebrow">能力 · Capabilities</div>
          <h2>四块能力,撑起一条中文 RAG + Agent 全链路</h2>
          <p>不是 demo 拼装,是从召回、编排、查询到评测都打通的工程实现。</p>
        </Reveal>
        <div className="cap-grid">
          {AE.caps.map((c,i)=>{ const Ico=capIcon[c.k]; return (
            <Reveal key={c.k} className="cap" delay={i*70}>
              <span className="num mono">0{i+1}</span>
              <span className="cap-ico"><Ico/></span>
              <div><h3>{c.t}</h3><p>{c.d}</p></div>
            </Reveal>
          );})}
        </div>
        <RagPipeline/>
      </div>
    </section>
  );
}

function RagPipeline(){
  const [ref,seen]=useInView({ threshold:0.4 });
  const [step,setStep]=useState(-1);
  useEffect(()=>{
    if(!seen) return;
    if(document.documentElement.dataset.motion==="off"){ setStep(99); return; }
    let i=0; setStep(0);
    const id=setInterval(()=>{ i++; setStep(i); if(i>AE.pipeline.length+1) i=0; }, 900);
    return ()=>clearInterval(id);
  },[seen]);
  const lit=(idx)=> step>=idx;
  return (
    <Reveal className="pipe" delay={120}>
      <div className="eyebrow" style={{marginBottom:18}}>双路召回 + RRF 融合 + BGE 重排</div>
      <div ref={ref} className="pipe-row">
        {AE.pipeline.map((n,idx)=>(
          <React.Fragment key={idx}>
            <div className="pipe-node">
              {n.split ? (
                <div className="pipe-split">
                  {n.split.map((s,si)=>(
                    <div key={si} className={`pipe-card ${lit(idx)?"lit":""}`}>
                      <span className="t">{s.t}</span><span className="v">{s.v}</span><span className="s">{s.s}</span>
                    </div>
                  ))}
                </div>
              ) : (
                <div className={`pipe-card ${lit(idx)?"lit":""}`}>
                  <span className="t">{n.t}</span><span className="v">{n.v}</span><span className="s">{n.s}</span>
                </div>
              )}
            </div>
            {idx<AE.pipeline.length-1 && <div className="pipe-arrow"><I.aRight/></div>}
          </React.Fragment>
        ))}
      </div>
      <p className="pipe-cap">中文问句进来 → BM25 与向量双路各召回 top-k → RRF 倒数排名融合去重 → BGE Reranker 精排截断 → 生成带页码引用的回答,低置信则兜底拒答。</p>
    </Reveal>
  );
}

/* ---------------- Metrics ---------------- */
function Metrics(){
  return (
    <section style={{padding:"0 0 8px"}}>
      <div className="wrap">
        <Reveal className="metrics">
          {AE.metrics.map((m,i)=>(
            <div className="metric" key={i}>
              <div className="v"><CountUp to={m.v} dec={m.dec||0} suf={m.suf||""}/></div>
              <div className="k">{m.k}</div><div className="d">{m.d}</div>
            </div>
          ))}
        </Reveal>
      </div>
    </section>
  );
}

/* ---------------- Trust ---------------- */
function Trust(){
  return (
    <section className="section-pad" id="open">
      <div className="wrap">
        <Reveal className="section-head">
          <div className="eyebrow">{AE.ui.trustEyebrow}</div>
          <h2>{AE.ui.trustTitle}</h2>
          <p>{AE.ui.trustLead}</p>
        </Reveal>
        <div className="trust-grid">
          {AE.trust.map((t,i)=>{ const Ico=trustIcon[t.icon];
            const inner=(
              <Reveal className="trust" as={t.link?"a":"div"} delay={i*80}
                {...(t.link?{href:t.link,target:"_blank",rel:"noreferrer"}:{})}>
                <div className="lbl">{t.lbl}</div>
                <div className="big accent" style={{color:"var(--accent)"}}>{t.big}</div>
                <div className="sub">{t.sub}{t.link && <span className="lk"><I.ext style={{width:14,height:14}}/></span>}</div>
              </Reveal>
            );
            return <React.Fragment key={i}>{inner}</React.Fragment>;
          })}
        </div>
      </div>
    </section>
  );
}

/* ---------------- About / Contact (terminal whoami) ---------------- */
function About(){
  const lines=[
    { cmd:"whoami" },
    { out:<><span className="accent">{AE.name}</span> · {AE.about.role}</> },
    { cmd:"cat focus.md" },
    { out:AE.about.blurb, comment:true },
    { cmd:"ls ./proof" },
    { proof:true },
    { cmd:"./contact --resume --email" },
    { contact:true },
  ];
  return (
    <section className="section-pad" id="contact">
      <div className="wrap">
        <div className="whoami">
          <div className="whoami-bar">
            <div className="tl"><i></i><i></i><i></i></div>
            <div className="addr mono">guoqi@ai-edge — ~/about — zsh</div>
            <div className="av mono">郭</div>
          </div>
          <div className="whoami-body mono">
            {lines.map((ln,i)=>(
              <div className="wt-line" key={i}>
                {ln.cmd && <div className="wt-cmd"><span className="p">$</span>{ln.cmd}</div>}
                {ln.out && <div className={`wt-out ${ln.comment?"cmt":""}`}>{ln.comment?"# ":""}{ln.out}</div>}
                {ln.proof && (
                  <div className="wt-proof">
                    <a href={AE.trust[0].link} target="_blank" rel="noreferrer"><span className="k">npm</span>{AE.npm}</a>
                    <a href={AE.trust[1].link} target="_blank" rel="noreferrer"><span className="k">git</span>{AE.github}</a>
                    <span className="pf"><span className="k">eval</span>Hit Rate@5 ≥ 90%</span>
                  </div>
                )}
                {ln.contact && (
                  <div className="wt-contact">
                    <a href={AE.resume} download="郭琦-AI应用-简历.pdf" className="btn btn-primary btn-sm"><I.download/>{AE.ui.downloadResume}</a>
                    <a href={"mailto:"+AE.email} className="wt-mail"><I.mail style={{width:15,height:15}}/>{AE.email}</a>
                  </div>
                )}
              </div>
            ))}
            <div className="wt-line">
              <span className="p">$</span><span className="wt-caret"></span>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

function Footer(){
  return (
    <footer className="foot">
      <div className="wrap foot-inner">
        <span>{AE.brand} · {AE.ui.footerTagline}</span>
        <span>{AE.npm} · MIT</span>
      </div>
    </footer>
  );
}

Object.assign(window, { I, useInView, Reveal, CountUp, Nav, Hero, Capabilities, Metrics, Trust, About, Footer });
