// app.jsx — Andrew Jiang personal site. Composes WaveField + paper-card sections.

const NOT_READY = true;

const CONTENT = {
  name: 'Andrew Jiang',
  pretitle: 'Personal Site · 2026',
  tagline: 'I build things people can feel.',
  intro: [
    "Hi, I'm Andrew. I'm 18, based in Oakville, and I build hardware that blurs the line between digital and physical.",
    "Right now: haptic sweaters that let you feel AR objects.",
  ],
  now: [
    { label: 'Building',  value: 'Caestus Labs — haptic sweaters for AR' },
    { label: 'Previously', value: 'Factful — 100k+ users, started at 15' },
    { label: 'Based in',  value: 'Oakville, Ontario' },
    { label: 'Reach out', value: 'andrew@jiang.now — I read every email' },
  ],
  projects: [
    {
      year: '2026', title: 'Caestus Labs', kind: 'Current · Pre-seed',
      blurb: "Haptic sweaters that let you feel AR objects. The thing I would have wanted to build at 13 and didn't have the skills for. Now I do.",
      href: 'https://caestuslabs.com',
    },
    {
      year: '2023–25', title: 'Factful', kind: 'Previous · 100k+ users',
      blurb: "Started as a fact-checker for writing — Grammarly on steroids. Pivoted to inverting the AI/human relationship: instead of AI fixing your work after the fact, it prompted you and gave feedback as you wrote. Built a custom RAG and text-parsing pipeline that was near-instant and accurate, and cracked the thing Grammarly's whole moat depends on — native underlines and popups inside Google Docs. Grew to 100,000+ users across beta and college pilots before shutting down over cofounder differences and weak conversion.",
      href: 'https://factful.io',
    },
  ],
  contacts: [
    { label: 'Email',    value: 'andrew@jiang.now',                 href: 'mailto:andrew@jiang.now' },
    { label: 'LinkedIn', value: 'linkedin.com/in/andrew-jiang1',    href: 'https://ca.linkedin.com/in/andrew-jiang1' },
    { label: 'GitHub',   value: 'github.com/Helloduck1234',         href: 'https://github.com/Helloduck1234' },
  ],
};

const ABOUT_PARAGRAPHS = [
  "I grew up in Oakville, Ontario, and got into computers the way a lot of people my age did — bored out of my mind during COVID. The first real thing I built was a fully functioning computer inside Minecraft, in grade 8. It worked. Mostly.",
  "That set the tone. I started with software because it was the easiest way to ship things from a bedroom. At 15, I founded Factful, which grew to over 100,000 users by the time I finished high school. But I always wanted to build things you could actually touch.",
  "That's what Caestus Labs is. We're building haptic wearables that let you physically feel AR objects — starting with sweaters. It's the thing I would have wanted to build at 13 and didn't have the skills for. Now I do.",
];

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

function useScrollProgress() {
  const [p, setP] = useState(0);
  useEffect(() => {
    let raf = 0, pending = false;
    const compute = () => {
      pending = false;
      const h = document.documentElement;
      const max = Math.max(1, h.scrollHeight - window.innerHeight);
      setP(Math.min(1, Math.max(0, window.scrollY / max)));
    };
    const onScroll = () => {
      if (pending) return;
      pending = true;
      raf = requestAnimationFrame(compute);
    };
    compute();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);
  return p;
}

function Nav() {
  const [time, setTime] = useState('');
  useEffect(() => {
    const tick = () => {
      const d = new Date();
      const hh = String(d.getHours()).padStart(2, '0');
      const mm = String(d.getMinutes()).padStart(2, '0');
      setTime(`${hh}:${mm} ET`);
    };
    tick();
    const iv = setInterval(tick, 30000);
    return () => clearInterval(iv);
  }, []);
  return (
    <nav className="nav">
      <div className="nav-inner">
        <a className="nav-mark" href="#top"><span className="mark-glyph">≋</span><span className="mark-text">andrew.jiang.now</span></a>
        <div className="nav-links">
          <a href="#about">About</a>
          <a href="#now">Now</a>
          <a href="#work">Work</a>
          <a href="#contact">Contact</a>
        </div>
        <div className="nav-meta"><span className="dot" /><span>{time}</span></div>
      </div>
    </nav>
  );
}

function Hero() {
  const [hbOpacity, setHbOpacity] = useState(1);
  useEffect(() => {
    let raf = 0, pending = false;
    const compute = () => {
      pending = false;
      const y = window.scrollY;
      const o = Math.max(0, Math.min(1, 1 - y / 140));
      setHbOpacity(o);
    };
    const onScroll = () => { if (pending) return; pending = true; raf = requestAnimationFrame(compute); };
    compute();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => { cancelAnimationFrame(raf); window.removeEventListener('scroll', onScroll); };
  }, []);
  return (
    <header className="hero" id="top">
      <div className="hero-inner">
        <div className="hero-meta"><span className="rule" /><span>{CONTENT.pretitle}</span></div>
        <h1 className="hero-name">
          <span className="hn-l1"><SurfaceText text="Andrew" ramp="ink" spread={0.55} enter={0.95} exit={0.45} /></span>
          <span className="hn-l2"><SurfaceText text="Jiang" ramp="ink" spread={0.55} enter={0.9} exit={0.4} /></span>
        </h1>
        <div className="hero-tagline">{CONTENT.tagline}</div>
        <div className="hero-intro">
          {CONTENT.intro.map((p, i) => <p key={i}>{p}</p>)}
        </div>
        <div className="hero-bottom" style={{ opacity: hbOpacity, transform: `translateY(${(1 - hbOpacity) * 8}px)`, pointerEvents: hbOpacity < 0.05 ? 'none' : 'auto' }}>
          <span className="hb-tag">↓ scroll · the sea is louder below</span>
          <span>43°27′N · 79°41′W</span>
        </div>
      </div>
    </header>
  );
}

function SectionHead({ index, kicker, title, ramp = 'classic' }) {
  return (
    <div className="sec-head">
      <div className="sec-index">{index}</div>
      <div>
        <div className="sec-kicker">{kicker}</div>
        <h2 className="sec-title"><SurfaceText text={title} ramp={ramp} spread={0.5} enter={0.85} exit={0.35} /></h2>
      </div>
    </div>
  );
}

function AboutSection() {
  return (
    <section className="section" id="about" data-section>
      <div className="section-inner">
        <SectionHead index="01" kicker="— About" title="About" ramp="ink" />
        <div className="about-body">
          {ABOUT_PARAGRAPHS.map((p, i) => <p key={i}>{p}</p>)}
        </div>
      </div>
    </section>
  );
}

function NowSection() {
  return (
    <section className="section" id="now" data-section>
      <div className="section-inner">
        <SectionHead index="02" kicker="— Right now" title="Now" ramp="waves" />
        <dl className="now-list">
          {CONTENT.now.map((row, i) => (
            <div className="now-row" key={i}>
              <dt>{row.label}</dt>
              <dd>{row.value}</dd>
            </div>
          ))}
        </dl>
        <div className="now-foot">Last updated: April 2026 · Updated when something meaningful changes.</div>
      </div>
    </section>
  );
}

function ProjectsSection() {
  return (
    <section className="section" id="work" data-section>
      <div className="section-inner">
        <SectionHead index="03" kicker="— Selected work" title="Work" ramp="foam" />
        <ul className="proj-list">
          {CONTENT.projects.map((p, i) => (
            <li className="proj" key={i}>
              <a className="proj-link" href={p.href} target="_blank" rel="noreferrer noopener">
                <div className="proj-year">{p.year}</div>
                <div className="proj-body">
                  <div className="proj-title">{p.title}</div>
                  <div className="proj-blurb">{p.blurb}</div>
                </div>
                <div className="proj-kind">{p.kind}</div>
                <div className="proj-arrow">↗</div>
              </a>
            </li>
          ))}
        </ul>
      </div>
    </section>
  );
}

function ContactSection() {
  return (
    <section className="section" id="contact" data-section>
      <div className="section-inner">
        <SectionHead index="04" kicker="— Get in touch" title="Say hi" ramp="dots" />
        <div className="contact-grid">
          <div className="contact-blurb">Best way to reach me is email. I read every one — usually within a day.</div>
          <ul className="contact-list">
            {CONTENT.contacts.map((c, i) => (
              <li key={i}>
                <a href={c.href} target={c.href.startsWith('mailto:') ? undefined : '_blank'} rel="noreferrer noopener">
                  <span className="cl-label">{c.label}</span>
                  <span className="cl-value">{c.value}</span>
                  <span className="cl-arrow">↗</span>
                </a>
              </li>
            ))}
          </ul>
        </div>
      </div>
    </section>
  );
}

function Footer() {
  return (
    <footer className="foot">
      <div className="foot-inner">
        <span>© {new Date().getFullYear()} Andrew Jiang</span>
        <span className="foot-glyph">≋ ~ ≋ ~ ≋</span>
        <span>Built on a wave</span>
      </div>
    </footer>
  );
}

function ScrollHint({ progress }) {
  return (
    <div className="scroll-hint">
      <div className="sh-track">
        <div className="sh-fill" style={{ height: `${(progress * 100).toFixed(1)}%` }} />
      </div>
    </div>
  );
}

function TweaksUI({ tweaks, setTweak }) {
  return (
    <TweaksPanel title="Wavefield">
      <TweakSection label="Field" />
      <TweakText label="Seed" value={tweaks.seed} onChange={(v) => setTweak('seed', v)} />
      <TweakSelect label="Ramp" value={tweaks.ramp} options={window.WAVE_RAMPS || ['classic']} onChange={(v) => setTweak('ramp', v)} />
      <TweakSlider label="Speed" value={tweaks.speed} min={0} max={3} step={0.05} onChange={(v) => setTweak('speed', v)} />
      <TweakSlider label="Amplitude" value={tweaks.amplitude} min={0.2} max={2} step={0.05} onChange={(v) => setTweak('amplitude', v)} />
      <TweakSlider label="Density" value={tweaks.density} min={0.5} max={1.6} step={0.05} onChange={(v) => setTweak('density', v)} />
      <TweakSlider label="Font size" value={tweaks.fontSize} min={10} max={28} step={1} unit="px" onChange={(v) => setTweak('fontSize', v)} />
    </TweaksPanel>
  );
}

function NotReady() {
  return (
    <div className="notready">
      <div className="notready-inner">
        <div className="nr-meta"><span className="rule" /><span>andrew.jiang.now</span></div>
        <h1 className="nr-title"><em>Still</em> building.</h1>
        <p className="nr-body">This site isn't ready yet. The waves got here first.</p>
        <p className="nr-foot">Come back soon · <a href="mailto:andrew@jiang.now">andrew@jiang.now</a></p>
      </div>
    </div>
  );
}

function App() {
  const canvasRef = useRef(null);
  const waveRef = useRef(null);
  const particlesRef = useRef(null);
  const progress = useScrollProgress();
  const isMobile = typeof window !== 'undefined' && window.innerWidth < 720;

  const [tweaks, setTweak] = useTweaks({
    seed: 'andrew',
    ramp: 'classic',
    speed: 1.0,
    amplitude: 1.0,
    density: isMobile ? 0.85 : 1.0,
    fontSize: isMobile ? 14 : 16,
  });

  // Initialize wave + particles
  useEffect(() => {
    const c = canvasRef.current; if (!c) return;
    const w = new WaveField(c, {
      seed: tweaks.seed,
      ramp: tweaks.ramp,
      speed: tweaks.speed,
      amplitude: tweaks.amplitude,
      density: tweaks.density,
      fontSize: tweaks.fontSize,
    });
    const p = new Particles({ fontSize: tweaks.fontSize });
    waveRef.current = w;
    particlesRef.current = p;
    w.onAfterDraw = (dt) => {
      p.update(dt);
      p.render(c.getContext('2d'), w.dpr);
    };
    w.start();
    return () => { w.stop(); w.onAfterDraw = null; waveRef.current = null; particlesRef.current = null; };
  }, []);

  // Sync tweaks → wave
  useEffect(() => {
    const w = waveRef.current; if (!w) return;
    w.setOpts({
      seed: tweaks.seed,
      ramp: tweaks.ramp,
      speed: tweaks.speed,
      amplitude: tweaks.amplitude,
      density: tweaks.density,
      fontSize: tweaks.fontSize,
    });
    if (particlesRef.current) particlesRef.current.setOpts({ fontSize: tweaks.fontSize });
  }, [tweaks.seed, tweaks.ramp, tweaks.speed, tweaks.amplitude, tweaks.density, tweaks.fontSize]);

  // Sync scroll
  useEffect(() => { if (waveRef.current) waveRef.current.setScroll(progress); }, [progress]);

  // Mouse + click → wave splash
  useEffect(() => {
    const c = canvasRef.current; if (!c) return;
    const onMove = (e) => waveRef.current && waveRef.current.setMouse(e.clientX, e.clientY, true);
    const onLeave = () => waveRef.current && waveRef.current.setMouse(0, 0, false);
    const onClick = (e) => {
      // ignore clicks on interactive elements; only background clicks splash
      const tag = (e.target && e.target.closest && e.target.closest('a,button,input,select,textarea'));
      if (tag) return;
      waveRef.current && waveRef.current.splash(e.clientX, e.clientY, 1);
    };
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseleave', onLeave);
    window.addEventListener('click', onClick);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseleave', onLeave);
      window.removeEventListener('click', onClick);
    };
  }, []);

  // Register section content as wave-quiet zones (waves part around text).
  useEffect(() => {
    const w = waveRef.current; if (!w) return;
    const sel = NOT_READY ? '.notready-inner' : '.section-inner';
    const els = Array.from(document.querySelectorAll(sel));
    w.setQuietElements(els, NOT_READY ? 110 : 90);
    return () => { if (waveRef.current) waveRef.current.setQuietElements([], 0); };
  }, []);

  // Particle vaporization on section exit (Agent 4)
  useEffect(() => {
    const sections = Array.from(document.querySelectorAll('[data-section]'));
    if (!sections.length) return;
    const wasVisible = new WeakMap();
    sections.forEach((s) => wasVisible.set(s, false));

    const io = new IntersectionObserver((entries) => {
      const p = particlesRef.current; if (!p) return;
      for (const e of entries) {
        const prev = wasVisible.get(e.target);
        const nowVisible = e.intersectionRatio > 0.15;
        // Trigger vaporize when section was visible but now leaving upward
        if (prev && !nowVisible && e.boundingClientRect.bottom < window.innerHeight * 0.5) {
          const reals = e.target.querySelectorAll('.st-real');
          const items = [];
          reals.forEach((node) => {
            const txt = node.textContent || '';
            if (!txt) return;
            const r = node.getBoundingClientRect();
            if (r.bottom < 0 || r.top > window.innerHeight) return;
            for (let i = 0; i < txt.length; i++) {
              const ch = txt[i];
              if (ch === ' ' || ch === '\u00A0') continue;
              items.push({
                x: r.left + (r.width / Math.max(1, txt.length)) * (i + 0.5),
                y: r.top + r.height / 2,
                char: ch,
              });
              if (items.length >= 120) break;
            }
            if (items.length >= 120) return;
          });
          if (items.length) p.spawn(items);
        }
        wasVisible.set(e.target, nowVisible);
      }
    }, { threshold: [0, 0.15, 0.5, 1] });

    sections.forEach((s) => io.observe(s));
    return () => io.disconnect();
  }, []);

  if (NOT_READY) {
    return (
      <div className="page">
        <canvas ref={canvasRef} className="waves" />
        <div className="paper-grain" />
        <NotReady />
        <TweaksUI tweaks={tweaks} setTweak={setTweak} />
      </div>
    );
  }

  return (
    <div className="page">
      <canvas ref={canvasRef} className="waves" />
      <div className="paper-grain" />
      <Nav />
      <Hero />
      <AboutSection />
      <NowSection />
      <ProjectsSection />
      <ContactSection />
      <Footer />
      <ScrollHint progress={progress} />
      <TweaksUI tweaks={tweaks} setTweak={setTweak} />
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
