/* Facere — cinematic scroll landing
   Three sections, three production-art backgrounds, one continuous camera push. */

const { useEffect, useRef, useState } = React;

// Mobile heuristic — captured once at boot. Used to skip expensive
// effects (extra particles, heavy scrub smoothing) on phones.
const IS_MOBILE =
  typeof window !== "undefined" &&
  window.matchMedia("(max-width: 760px), (pointer: coarse) and (max-width: 900px)").matches;

/* ───────────────── PixelLogo ─────────────────
   Bitmap glyphs rendered as a CSS grid of <span>s, so we control individual
   pixel colors / glow / animations. Original blocky design (no copyrighted mark). */
// 7 wide × 9 tall chunky glyphs, designed in the style of the reference:
// thick stems, rounded/beveled corners on C/E/R, no diagonals.
const GLYPH_W = 7;
const GLYPH_H = 9;
const GLYPHS = {
  F: [
    "#######",
    "#######",
    "##.....",
    "##.....",
    "######.",
    "######.",
    "##.....",
    "##.....",
    "##.....",
  ],
  A: [
    ".#####.",
    "#######",
    "##...##",
    "##...##",
    "#######",
    "#######",
    "##...##",
    "##...##",
    "##...##",
  ],
  C: [
    ".######",
    "#######",
    "##.....",
    "##.....",
    "##.....",
    "##.....",
    "##.....",
    "#######",
    ".######",
  ],
  E: [
    "#######",
    "#######",
    "##.....",
    "##.....",
    "######.",
    "######.",
    "##.....",
    "#######",
    "#######",
  ],
  R: [
    "######.",
    "#######",
    "##...##",
    "##...##",
    "######.",
    "#####..",
    "##.##..",
    "##..##.",
    "##...##",
  ],
};

function useResponsiveLogoScale(word = "FACERE") {
  const [scale, setScale] = useState(() => computeLogoScale(word));
  useEffect(() => {
    let raf = 0;
    const onResize = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => setScale(computeLogoScale(word)));
    };
    window.addEventListener("resize", onResize);
    window.addEventListener("orientationchange", onResize);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", onResize);
      window.removeEventListener("orientationchange", onResize);
    };
  }, [word]);
  return scale;
}

function computeLogoScale(word) {
  // Total logo width = letters * GLYPH_W * scale + (letters-1) * scale * 1.6
  // Solve for scale that fits the viewport with side margins.
  if (typeof window === "undefined") return 11;
  const vw = window.innerWidth;
  const letters = word.length;
  const sideMargin = vw < 480 ? 0.10 : vw < 760 ? 0.12 : 0.16; // each side
  const usable = vw * (1 - 2 * sideMargin);
  const denom = letters * GLYPH_W + (letters - 1) * 1.6;
  const fit = Math.floor(usable / denom);
  // Clamp so it doesn't get too small or absurdly large on ultrawide.
  return Math.max(4, Math.min(11, fit));
}

function PixelLogo({ word = "FACERE", scale, gap = 0 }) {
  const responsiveScale = useResponsiveLogoScale(word);
  const px = scale ?? responsiveScale;
  const letters = word.split("");
  return (
    <div className="pixel-logo" data-text={word}>
      <div className="pixel-logo-grid" style={{ gap: `0 ${px * 1.6}px` }}>
        {letters.map((ch, li) => {
          const g = GLYPHS[ch];
          if (!g) return null;
          return (
            <div
              key={li}
              className="pixel-letter"
              style={{
                gridTemplateColumns: `repeat(${GLYPH_W}, ${px}px)`,
                gridTemplateRows: `repeat(${GLYPH_H}, ${px}px)`,
                gap: `${gap}px`,
              }}
            >
              {g.flatMap((row, ri) =>
                row.split("").map((c, ci) => (
                  <span
                    key={`${ri}-${ci}`}
                    className={c === "#" ? "px on" : "px"}
                  />
                ))
              )}
            </div>
          );
        })}
      </div>
      {/* Ghost layers for subtle electric jitter */}
      <div className="pixel-logo-ghost ghost-r" aria-hidden="true">
        <PixelLogoStatic word={word} scale={px} gap={gap} color="rgba(255,80,120,0.45)" />
      </div>
      <div className="pixel-logo-ghost ghost-c" aria-hidden="true">
        <PixelLogoStatic word={word} scale={px} gap={gap} color="rgba(33,234,255,0.55)" />
      </div>
    </div>
  );
}

function PixelLogoStatic({ word, scale, gap, color }) {
  const letters = word.split("");
  return (
    <div className="pixel-logo-grid" style={{ gap: `0 ${scale * 1.6}px` }}>
      {letters.map((ch, li) => {
        const g = GLYPHS[ch];
        if (!g) return null;
        return (
          <div
            key={li}
            className="pixel-letter"
            style={{
              gridTemplateColumns: `repeat(${GLYPH_W}, ${scale}px)`,
              gridTemplateRows: `repeat(${GLYPH_H}, ${scale}px)`,
              gap: `${gap}px`,
            }}
          >
            {g.flatMap((row, ri) =>
              row.split("").map((c, ci) => (
                <span
                  key={`${ri}-${ci}`}
                  className="px"
                  style={
                    c === "#"
                      ? { background: color, boxShadow: "none" }
                      : { background: "transparent" }
                  }
                />
              ))
            )}
          </div>
        );
      })}
    </div>
  );
}

/* ───────────────── SmokeLayer ─────────────────
   Pixel-art smoke puffs above each chimney, positioned as % over the bg image.
   Each puff is a chunky, low-res-feeling translucent disc that drifts up + fades. */
function SmokeLayer({ chimneys, scale = 1, intensity = 1 }) {
  // Each chimney emits N puffs on staggered phases.
  const PUFFS_PER = IS_MOBILE ? 2 : 3;
  const puffs = [];
  chimneys.forEach((ch, ci) => {
    for (let p = 0; p < PUFFS_PER; p++) {
      const delay = (p / PUFFS_PER) * ch.dur + (ci * 0.3);
      puffs.push({
        id: `${ci}-${p}`,
        x: ch.x,
        y: ch.y,
        size: ch.size * (0.85 + (p % 2) * 0.25),
        dur: ch.dur,
        delay,
        drift: ch.drift + (p % 3 - 1) * 0.4,
      });
    }
  });

  return (
    <div className="smoke-layer" style={{ "--smoke-scale": scale, "--smoke-intensity": intensity }}>
      {puffs.map((p) => (
        <span
          key={p.id}
          className="smoke-puff"
          style={{
            left: `${p.x}%`,
            top: `${p.y}%`,
            width: `${p.size}px`,
            height: `${p.size}px`,
            animationDuration: `${p.dur}s`,
            animationDelay: `-${p.delay}s`,
            "--drift": `${p.drift}rem`,
          }}
        />
      ))}
    </div>
  );
}

/* ───────────────── EmberLayer ─────────────────
   Tiny pulsing orange points scattered across the horizon — gives the city
   /factory a flicker without recreating any structure in CSS. */
function EmberLayer({ count = 18, area }) {
  const embers = useRef(null);
  if (!embers.current) {
    const arr = [];
    for (let i = 0; i < count; i++) {
      arr.push({
        x: area.x + Math.random() * area.w,
        y: area.y + Math.random() * area.h,
        size: 2 + Math.random() * 2,
        dur: 1.6 + Math.random() * 3,
        delay: Math.random() * 4,
        hue: Math.random() > 0.7 ? "warm" : "soft",
      });
    }
    embers.current = arr;
  }
  return (
    <div className="ember-layer">
      {embers.current.map((e, i) => (
        <span
          key={i}
          className={`ember ember-${e.hue}`}
          style={{
            left: `${e.x}%`,
            top: `${e.y}%`,
            width: `${e.size}px`,
            height: `${e.size}px`,
            animationDuration: `${e.dur}s`,
            animationDelay: `-${e.delay}s`,
          }}
        />
      ))}
    </div>
  );
}

/* ───────────────── SparkLayer ─────────────────
   For factory interior — small orange sparks bursting from the welding zone. */
function SparkLayer() {
  const sparks = useRef(null);
  if (!sparks.current) {
    const arr = [];
    const count = IS_MOBILE ? 7 : 14;
    for (let i = 0; i < count; i++) {
      arr.push({
        a: -90 + (Math.random() - 0.5) * 160, // angle deg
        d: 18 + Math.random() * 36,           // distance
        size: 2 + Math.random() * 2,
        dur: 0.6 + Math.random() * 1.0,
        delay: Math.random() * 1.6,
      });
    }
    sparks.current = arr;
  }
  return (
    <div className="spark-layer">
      <span className="spark-core" />
      {sparks.current.map((s, i) => {
        const rad = (s.a * Math.PI) / 180;
        const tx = Math.cos(rad) * s.d;
        const ty = Math.sin(rad) * s.d;
        return (
          <span
            key={i}
            className="spark"
            style={{
              "--tx": `${tx}px`,
              "--ty": `${ty}px`,
              width: `${s.size}px`,
              height: `${s.size}px`,
              animationDuration: `${s.dur}s`,
              animationDelay: `-${s.delay}s`,
            }}
          />
        );
      })}
    </div>
  );
}

/* ───────────────── ChipPulses ─────────────────
   Subtle pulsing orange dots over the PCB area. */
function ChipPulses() {
  // hand-placed % over the chip image
  const allDots = [
    { x: 76, y: 38, dur: 2.4, delay: 0 },
    { x: 82, y: 28, dur: 2.0, delay: 0.7 },
    { x: 88, y: 33, dur: 2.8, delay: 1.2 },
    { x: 73, y: 60, dur: 2.2, delay: 0.4 },
    { x: 80, y: 75, dur: 2.6, delay: 1.0 },
    { x: 92, y: 55, dur: 2.0, delay: 1.6 },
    { x: 70, y: 48, dur: 3.0, delay: 0.2 },
    { x: 95, y: 70, dur: 2.4, delay: 0.9 },
  ];
  const dots = IS_MOBILE ? allDots.filter((_, i) => i % 2 === 0) : allDots;
  return (
    <div className="chip-pulses">
      {dots.map((d, i) => (
        <span
          key={i}
          className="chip-pulse"
          style={{
            left: `${d.x}%`,
            top: `${d.y}%`,
            animationDuration: `${d.dur}s`,
            animationDelay: `-${d.delay}s`,
          }}
        />
      ))}
      {/* Soft chip breathing glow */}
      <span className="chip-glow" />
    </div>
  );
}

/* ───────────────── HeroSection ───────────────── */
function HeroSection({ bgRef, contentRef }) {
  // Chimney positions (% over the exterior factory image) — hand-tuned to artwork.
  // The three tall chimneys cluster at ~74%, 78%, 83% with tops near y≈49.5%.
  // Smaller stacks at 71%, 80%, 85.5%.
  // Image 1: assets/exterior-factory.png
  const chimneys = [
    { x: 71.5, y: 51.0, size: 22, dur: 7.2, drift: -0.3 },
    { x: 74.5, y: 49.5, size: 32, dur: 8.0, drift: -0.4 },
    { x: 78.5, y: 50.4, size: 30, dur: 7.6, drift: 0.2 },
    { x: 80.5, y: 51.0, size: 22, dur: 7.0, drift: 0.3 },
    { x: 83.5, y: 49.8, size: 32, dur: 8.2, drift: -0.2 },
    { x: 86.0, y: 51.4, size: 22, dur: 7.4, drift: 0.4 },
  ];

  return (
    <section className="scene scene-hero" data-screen-label="01 Hero">
      <div className="bg-wrap" ref={bgRef}>
        <div className="image-frame">
          {/* Image 1 — exterior factory */}
          <div
            className="bg-image"
            style={{ backgroundImage: "url(assets/exterior-factory.png)" }}
          />
          <SmokeLayer chimneys={IS_MOBILE ? chimneys.filter((_, i) => i % 2 === 0) : chimneys} />
          <EmberLayer
            count={IS_MOBILE ? 10 : 22}
            area={{ x: 32, y: 48, w: 65, h: 8 }}
          />
        </div>
        <div className="vignette" />
        <div className="haze" />
      </div>

      <div className="content content-hero" ref={contentRef}>
        <div className="logo-wrap">
          <PixelLogo word="FACERE" />
          <div className="logo-scanlines" />
        </div>
      </div>

      <div className="scroll-cue" aria-hidden="true">
        <span className="scroll-cue-line" />
        <span className="scroll-cue-label">SCROLL</span>
      </div>
    </section>
  );
}

/* ───────────────── ProductSection ───────────────── */
function ProductSection({ bgRef, contentRef }) {
  return (
    <section className="scene scene-product" data-screen-label="02 Product">
      <div className="bg-wrap" ref={bgRef}>
        <div className="image-frame">
          {/* Image 2 — factory interior / assembly line */}
          <div
            className="bg-image"
            style={{ backgroundImage: "url(assets/factory-interior.png)" }}
          />
          {/* Robotic arm idle motion: anchored to where the arms sit in the image */}
          <div className="arm-pivot arm-left" />
          <div className="arm-pivot arm-right" />
          <SparkLayer />
          {/* Conveyor pulse strip */}
          <div className="conveyor-pulse" />
        </div>
        <div className="vignette product-vignette" />
        <div className="haze" />
      </div>

      <div className="content content-product" ref={contentRef}>
        <div className="tag">
          <span className="tag-dot" />
          <span>SYSTEM ONLINE — NODE 02</span>
        </div>
        <h1 className="headline">
          Facere is a
          <br />
          <span className="hl">Hardware Design Agent</span>
        </h1>
        <p className="body">
          From natural-language requirements to engineering files, Facere executes an end-to-end agent workflow
        </p>
      </div>
    </section>
  );
}

/* ───────────────── InstallSection ───────────────── */
function InstallSection({ bgRef, contentRef }) {
  const [copied, setCopied] = useState(false);
  const cmd = "curl -fsSL https://facere.ai/install.sh | sh";

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(cmd);
      setCopied(true);
      setTimeout(() => setCopied(false), 1600);
    } catch (e) {
      setCopied(true);
      setTimeout(() => setCopied(false), 1600);
    }
  };

  return (
    <section className="scene scene-install" data-screen-label="03 Install">
      <div className="bg-wrap" ref={bgRef}>
        <div className="image-frame">
          {/* Image 3 — PCB / chip */}
          <div
            className="bg-image"
            style={{ backgroundImage: "url(assets/pcb-chip.png)" }}
          />
          <ChipPulses />
        </div>
        <div className="vignette" />
        <div className="haze haze-deep" />
      </div>

      <div className="content content-install" ref={contentRef}>
        <div className="tag">
          <span className="tag-dot" />
          <span>NODE 03 — SUBSTRATE</span>
        </div>
        <h1 className="headline">
          Get started with Facere
        </h1>
        <p className="subheadline">Install the CLI</p>

        <div className={`cmd-box ${copied ? "copied" : ""}`} onClick={copy}>
          <div className="cmd-bar">
            <span className="cmd-led" />
            <span className="cmd-led led-amber" />
            <span className="cmd-led led-cyan" />
            <span className="cmd-title">~/facere — bash</span>
          </div>
          <div className="cmd-body">
            <span className="cmd-prompt">$</span>
            <span className="cmd-text">{cmd}</span>
            <span className="cmd-cursor" />
          </div>
          <div className="cmd-hint">
            {copied ? "✓ COPIED" : "CLICK TO COPY"}
          </div>
        </div>

        <a className="docs-link" href="#" onClick={(e) => e.preventDefault()}>
          docs.facere.ai/cli <span className="arrow">→</span>
        </a>
      </div>
    </section>
  );
}

/* ───────────────── App / ScrollScene ─────────────────
   Pin the stage, push the camera deeper through each section.
   Each section's bg scales up while the next fades in over it. */
function App() {
  const stageRef = useRef(null);
  const heroBg = useRef(null), heroContent = useRef(null);
  const prodBg = useRef(null), prodContent = useRef(null);
  const instBg = useRef(null), instContent = useRef(null);
  const heroSceneRef = useRef(null);
  const prodSceneRef = useRef(null);
  const instSceneRef = useRef(null);

  const [progress, setProgress] = useState(0);
  const lastDepth = useRef(-1);

  useEffect(() => {
    if (!window.gsap || !window.ScrollTrigger) return;
    const { gsap, ScrollTrigger } = window;
    gsap.registerPlugin(ScrollTrigger);

    const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    // Initial state: only hero visible
    gsap.set(prodSceneRef.current, { opacity: 0 });
    gsap.set(instSceneRef.current, { opacity: 0 });

    const ctx = gsap.context(() => {
      // Master timeline pinned to stage; scrub linked to scroll.
      const tl = gsap.timeline({
        scrollTrigger: {
          trigger: ".scroll-track",
          start: "top top",
          end: "bottom bottom",
          // On mobile, scrub:true (no smoothing lag) feels snappier and
          // skips an extra rAF interpolation per frame.
          scrub: IS_MOBILE ? true : 0.6,
          onUpdate: (self) => {
            // Re-render only when the displayed depth (0..100) changes —
            // otherwise we re-render the whole HUD on every scroll frame.
            const depth = Math.round(self.progress * 100);
            if (depth !== lastDepth.current) {
              lastDepth.current = depth;
              setProgress(self.progress);
            }
          },
        },
      });

      // Timing map (extra-long product dwell):
      //   0.00 → 0.20   Hero camera push
      //   0.20 → 0.25   Hero → Product cross-fade
      //   0.25 → 0.82   Product (very long dwell)
      //   0.82 → 0.86   Product → Install cross-fade
      //   0.86 → 1.00   Install settle

      // Phase 1: Hero camera push-in
      tl.to(heroBg.current, { scale: reduceMotion ? 1.05 : 1.35, ease: "none" }, 0);
      tl.to(heroContent.current, { scale: reduceMotion ? 1.02 : 1.18, opacity: 0, ease: "none" }, 0);

      // Phase 2: cross-fade hero → product
      tl.to(heroSceneRef.current, { opacity: 0, ease: "none" }, 0.20);
      tl.fromTo(prodSceneRef.current, { opacity: 0 }, { opacity: 1, ease: "none" }, 0.20);
      tl.fromTo(prodBg.current, { scale: reduceMotion ? 1.05 : 1.18 }, { scale: 1, ease: "none" }, 0.20);
      tl.fromTo(prodContent.current, { y: 40, opacity: 0 }, { y: 0, opacity: 1, ease: "none" }, 0.23);

      // Phase 3: Long product dwell with slow camera creep
      tl.to(prodBg.current, { scale: reduceMotion ? 1.05 : 1.22, ease: "none" }, 0.27);
      tl.to(prodContent.current, { scale: reduceMotion ? 1.02 : 1.08, opacity: 0, ease: "none", duration: 0.06 }, 0.78);

      // Phase 4: cross-fade product → install
      tl.to(prodSceneRef.current, { opacity: 0, ease: "none" }, 0.82);
      tl.fromTo(instSceneRef.current, { opacity: 0 }, { opacity: 1, ease: "none" }, 0.82);
      tl.fromTo(instBg.current, { scale: reduceMotion ? 1.05 : 1.18 }, { scale: 1, ease: "none" }, 0.82);
      tl.fromTo(instContent.current, { y: 40, opacity: 0 }, { y: 0, opacity: 1, ease: "none" }, 0.86);

      // Phase 5: Install gentle settle
      tl.to(instBg.current, { scale: reduceMotion ? 1.0 : 1.05, ease: "none" }, 0.90);
    }, stageRef);

    return () => ctx.revert();
  }, []);

  // Section indicator
  const section = progress < 0.22 ? 0 : progress < 0.84 ? 1 : 2;

  return (
    <>
      {/* Persistent UI: section indicator + brand mark in corner */}
      <div className="hud">
        <div className="hud-brand">FACERE</div>
        <div className="hud-nav" aria-hidden="true">
          {["EXT", "INT", "SUB"].map((label, i) => (
            <div key={i} className={`hud-step ${section === i ? "is-active" : section > i ? "is-past" : ""}`}>
              <span className="hud-step-dot" />
              <span className="hud-step-label">{label}</span>
            </div>
          ))}
        </div>
        <div className="hud-meta">
          <span className="hud-meta-line" />
          <span>DEPTH {Math.round(progress * 100).toString().padStart(2, "0")}</span>
        </div>
      </div>

      {/* Scroll track sets total scrollable height; stage is pinned & sticky */}
      <div className="scroll-track">
        <div className="stage" ref={stageRef}>
          <div className="scene-stack">
            <div className="scene-slot" ref={heroSceneRef}>
              <HeroSection bgRef={heroBg} contentRef={heroContent} />
            </div>
            <div className="scene-slot" ref={prodSceneRef}>
              <ProductSection bgRef={prodBg} contentRef={prodContent} />
            </div>
            <div className="scene-slot" ref={instSceneRef}>
              <InstallSection bgRef={instBg} contentRef={instContent} />
            </div>
          </div>
          <div className="grain" />
        </div>
      </div>
    </>
  );
}

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