// The floating pill, ported 1:1 from the app's RecordingIndicatorWindow.swift.
// Geometry, colors, eye shapes, wave math, and timings all mirror the Swift
// source so the site shows exactly what ships.
const { useState: useStateP, useEffect: useEffectP, useRef: useRefP } = React;

// NinjaTheme constants
const PILL_ACCENT = "#E8463E";                 // NinjaTheme.accent
const PILL_DARK = "rgb(26, 26, 31)";           // NinjaTheme.dark (0.1, 0.1, 0.12)
const PILL_SUCCESS = "rgba(53, 122, 77, 0.9)"; // NinjaTheme.success at 0.9

const PILL_SCALE = 2; // rendered at 2x so the web crowd can actually see it

// NinjaMask shape from the app, sampled into an SVG path (viewBox 100x62):
// wide butterfly mask with the W-shaped bottom edge (nose bridge dip)
const MASK_PATH = "M 0 27.9 C 5 0, 25 0, 50 3.1 C 75 0, 95 0, 100 27.9 C 102 44.6, 88 58.9, 72 57 C 62 55.8, 55 38.4, 50 38.4 C 45 38.4, 38 55.8, 28 57 C 12 58.9, -2 44.6, 0 27.9 Z";

// NinjaEye: sharp angular slit. Generic path for a w x h box, left eye;
// right eye is the mirror.
function eyePath(w, h, flipped) {
  if (!flipped) {
    return `M 0 ${h * 0.1} L ${w * 0.85} ${h * 0.35} L ${w} ${h * 0.5} Q ${w * 0.35} ${h} 0 ${h * 0.1} Z`;
  }
  return `M ${w} ${h * 0.1} L ${w * 0.15} ${h * 0.35} L 0 ${h * 0.5} Q ${w * 0.65} ${h} ${w} ${h * 0.1} Z`;
}
// NinjaHappyEye: upward arc, stroked
const happyPath = (w, h) => `M 0 ${h * 0.7} Q ${w * 0.5} ${-h * 0.3} ${w} ${h * 0.7}`;

function EyeSvg({ w, h, flipped, color, s = PILL_SCALE }) {
  return (
    <svg width={w * s} height={h * s} viewBox={`0 0 ${w} ${h}`} style={{ display: "block" }} aria-hidden>
      <path d={eyePath(w, h, flipped)} fill={color} />
    </svg>
  );
}
function HappyEyeSvg({ w, h, flipped, color, s = PILL_SCALE }) {
  return (
    <svg width={w * s} height={h * s} viewBox={`0 0 ${w} ${h}`} style={{ display: "block", overflow: "visible" }} aria-hidden>
      <path d={happyPath(w, h)} fill="none" stroke={color} strokeWidth="2.5" strokeLinecap="round" transform={flipped ? `scale(-1,1) translate(${-w},0)` : undefined} />
    </svg>
  );
}

// The pill itself. state: idle | recording | processing | result
// level: simulated audio level (app reads the mic; we fake the same range)
function NinjaPill({ state, level, phase, hovering, scale = PILL_SCALE }) {
  const S = scale;
  const isRecording = state === "recording";
  const isProcessing = state === "processing";
  const isResult = state === "result";
  const isActive = state !== "idle";

  // ninjaFace math, verbatim from Swift
  const glow = isRecording ? Math.min(1, level * 8) : 0;
  const w = (isActive ? 64 : 56) * S;
  const h = (isActive ? 40 : 34) * S;
  const glowColor = isResult ? PILL_SUCCESS : PILL_ACCENT;
  const glowRgb = isResult ? "53, 122, 77" : "232, 70, 62";
  const glowOpacity = isRecording ? 0.12 + 0.12 * glow : (isResult ? 0.18 : 0);
  const shadowOpacity = isRecording ? 0.3 : (isResult ? 0.35 : 0);
  const hoodOpacity = isActive ? 0.82 : (hovering ? 0.7 : 0.55);
  const maskW = w * 0.82;
  const maskH = h * 0.62;
  const eyeSpacing = maskW * 0.22;

  // waveBarHeight / processingDotOpacity, verbatim
  const waveBarHeight = (i) => {
    const p = phase + i * 0.7;
    const wave = (Math.sin(p) + 1) / 2 * 0.7 + 0.3;
    return Math.max(2, Math.min(10, level * 140 * wave));
  };
  const dotOpacity = (i) => {
    const p = phase + i * 0.8;
    return (Math.sin(p) + 1) / 2 * 0.6 + 0.3;
  };

  const spring = "all 350ms cubic-bezier(0.34, 1.3, 0.5, 1)"; // spring(0.35, 0.75)
  const eyeRow = (children, visible) => (
    <div style={{
      position: "absolute", inset: 0, display: "flex", gap: eyeSpacing,
      alignItems: "center", justifyContent: "center",
      opacity: visible ? 1 : 0, transition: "opacity 200ms ease",
    }}>{children}</div>
  );

  return (
    <div className="pill-stack" style={{ transform: hovering && !isActive ? "scale(1.06)" : "scale(1)", transition: "transform 200ms ease-in-out" }}>
      <div className="pill-face" style={{ position: "relative", width: w + 10 * S, height: h + 8 * S, display: "grid", placeItems: "center", transition: spring }}>
        {/* state glow */}
        <div style={{
          position: "absolute", width: w + 10 * S, height: h + 8 * S,
          borderRadius: 16 * S, background: glowColor, opacity: glowOpacity,
          transition: spring,
        }} />
        {/* dark hood */}
        <div style={{
          position: "absolute", width: w, height: h,
          borderRadius: (isActive ? 12 : 10) * S,
          background: PILL_DARK, opacity: hoodOpacity,
          boxShadow: shadowOpacity ? `0 0 ${6 * S}px rgba(${glowRgb}, ${shadowOpacity})` : "none",
          transition: spring,
        }} />
        {/* white mask */}
        <svg width={maskW} height={maskH} viewBox="0 0 100 62" preserveAspectRatio="none"
          style={{ position: "absolute", transform: `translateY(${h * 0.02}px)`, transition: spring }} aria-hidden>
          <path d={MASK_PATH} fill={`rgba(255,255,255,${isActive ? 0.97 : (hovering ? 0.95 : 0.92)})`} />
        </svg>
        {/* eyes: all four expressions stay mounted and crossfade, like the app */}
        <div style={{ position: "absolute", width: maskW, height: maskH, transform: `translateY(${-h * 0.02}px)` }}>
          {eyeRow(<>
            <EyeSvg w={13 + 1.5 * glow} h={6 + 4 * glow} flipped={false} color={PILL_DARK} s={S} />
            <EyeSvg w={13 + 1.5 * glow} h={6 + 4 * glow} flipped={true} color={PILL_DARK} s={S} />
          </>, isRecording)}
          {eyeRow(<>
            <EyeSvg w={11} h={2.5} flipped={false} color={PILL_DARK} s={S} />
            <EyeSvg w={11} h={2.5} flipped={true} color={PILL_DARK} s={S} />
          </>, isProcessing)}
          {eyeRow(<>
            <HappyEyeSvg w={11} h={5} flipped={false} color={PILL_DARK} s={S} />
            <HappyEyeSvg w={11} h={5} flipped={true} color={PILL_DARK} s={S} />
          </>, isResult)}
          {eyeRow(<>
            <EyeSvg w={11} h={3.5} flipped={false} color={PILL_DARK} s={S} />
            <EyeSvg w={11} h={3.5} flipped={true} color={PILL_DARK} s={S} />
          </>, !isActive)}
        </div>
      </div>

      {/* sub-indicator: wave bars while recording, dots while processing */}
      <div style={{ height: (isRecording || isProcessing ? 12 : 0) * S, display: "flex", alignItems: "center", justifyContent: "center", transition: spring, overflow: "visible" }}>
        {isRecording && (
          <div style={{ display: "flex", gap: 1.5 * S, alignItems: "center", height: 10 * S }}>
            {[0, 1, 2, 3, 4, 5, 6].map(i => (
              <div key={i} style={{
                width: 2 * S, height: waveBarHeight(i) * S,
                borderRadius: S, background: PILL_ACCENT, opacity: 0.85,
              }} />
            ))}
          </div>
        )}
        {isProcessing && (
          <div style={{ display: "flex", gap: 3 * S }}>
            {[0, 1, 2].map(i => (
              <div key={i} style={{
                width: 3.5 * S, height: 3.5 * S,
                borderRadius: "50%", background: PILL_ACCENT, opacity: dotOpacity(i),
              }} />
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

// ————————————————————————————————————————————————————————————
// Showcase: a mini desktop where the pill lives above the Dock.
// Use cases play one after another: coding with Claude in the
// terminal, a Slack message, an email — raw mumble in, clean text
// out. Hold Space (or the pill) to drive a cycle yourself.
// ————————————————————————————————————————————————————————————
const PILL_SCENES = [
  {
    id: "terminal",
    kind: "terminal",
    label: "Terminal · Claude Code",
    raw: "um refactor the auth middleware and uh run the tests again",
    clean: "Refactor the auth middleware and run the tests again.",
  },
  {
    id: "slack",
    kind: "slack",
    label: "Slack · #launch-team",
    raw: "hey um the build is green like ship it whenever youre ready",
    clean: "Hey, the build is green. Ship it whenever you're ready!",
  },
  {
    id: "mail",
    kind: "mail",
    label: "Mail · new message",
    raw: "hi uh just checking in on the um invoice from last month thanks",
    clean: "Hi, just checking in on the invoice from last month. Thanks!",
  },
];

// One fake app window. mode: "raw" while dictating (grey, unpolished),
// "clean" once the ninja has typed the real thing.
function PillAppWindow({ scene, mode, text, active }) {
  const caret = <span className="pw-caret" />;
  const body = (
    <span className={mode === "raw" ? "pw-raw" : "pw-clean"}>{text}{active && caret}</span>
  );
  if (scene.kind === "terminal") {
    return (
      <div className="pw pw-terminal" key={scene.id}>
        <div className="pw-bar pw-bar-dark"><span className="pw-bardots" aria-hidden /><span>Terminal — claude</span></div>
        <div className="pw-term-body">
          <div className="pw-term-dim">❯ claude</div>
          <div className="pw-term-dim">Claude Code · ready</div>
          <div>&gt; {body}</div>
        </div>
      </div>
    );
  }
  if (scene.kind === "slack") {
    return (
      <div className="pw pw-light" key={scene.id}>
        <div className="pw-bar"><span className="pw-bardots" aria-hidden /><span># launch-team — Slack</span></div>
        <div className="pw-body">
          <div className="pw-prior"><strong>sam</strong> deploy's ready when you are 🚀</div>
          <div className="pw-input">{body}</div>
        </div>
      </div>
    );
  }
  return (
    <div className="pw pw-light" key={scene.id}>
      <div className="pw-bar"><span className="pw-bardots" aria-hidden /><span>New Message — Mail</span></div>
      <div className="pw-body">
        <div className="pw-prior">To: client@big.co &nbsp;·&nbsp; Subject: Invoice</div>
        <div className="pw-input">{body}</div>
      </div>
    </div>
  );
}

function PillShowcase() {
  const [state, setState] = useStateP("idle");
  const [level, setLevel] = useStateP(0);
  const [phase, setPhase] = useStateP(0);
  const [hovering, setHovering] = useStateP(false);
  const [everHeld, setEverHeld] = useStateP(false);
  const [sceneIdx, setSceneIdx] = useStateP(0);
  const [winMode, setWinMode] = useStateP("raw");
  const [winText, setWinText] = useStateP("");
  const holdRef = useRefP(false);
  const stateRef = useRefP("idle");
  const sceneRef = useRefP(0);
  const timers = useRefP([]);
  stateRef.current = state;
  sceneRef.current = sceneIdx;

  const later = (fn, ms) => timers.current.push(setTimeout(fn, ms));
  const clearTimers = () => { timers.current.forEach(clearTimeout); timers.current = []; };
  const scene = PILL_SCENES[sceneIdx];

  // same 60ms / +0.25 animation clock as the app, with a fake mic level
  useEffectP(() => {
    if (state !== "recording" && state !== "processing") return;
    const id = setInterval(() => {
      setPhase(p => p + 0.25);
      const t = performance.now() / 1000;
      setLevel(Math.abs(Math.sin(t * 2.1) * Math.sin(t * 3.7)) * 0.05 + 0.02);
    }, 60);
    return () => clearInterval(id);
  }, [state]);

  const streamText = (full, ms, then) => {
    full.split("").forEach((_, i) => later(() => setWinText(full.slice(0, i + 1)), i * ms));
    later(then, full.length * ms + 250);
  };

  // release-to-result tail, shared by auto loop and manual holds
  const finishCycle = (idx) => {
    setState("processing");
    later(() => {
      setState("result");
      setWinMode("clean");
      setWinText("");
      streamText(PILL_SCENES[idx].clean, 22, () => {
        later(() => setState("idle"), 500);
        later(() => playScene((idx + 1) % PILL_SCENES.length), 2600);
      });
    }, 1000);
  };

  const playScene = (idx) => {
    clearTimers();
    setSceneIdx(idx);
    setWinMode("raw");
    setWinText("");
    setState("recording");
    streamText(PILL_SCENES[idx].raw, 46, () => {
      if (holdRef.current) return; // user took over mid-scene
      finishCycle(idx);
    });
  };

  const startHold = () => {
    if (holdRef.current) return;
    holdRef.current = true;
    setEverHeld(true);
    clearTimers();
    const idx = sceneRef.current;
    setWinMode("raw");
    setWinText("");
    setState("recording");
    // your "speech" is the scene's raw transcript, streaming while you hold
    PILL_SCENES[idx].raw.split("").forEach((_, i) =>
      later(() => setWinText(PILL_SCENES[idx].raw.slice(0, i + 1)), i * 46)
    );
  };
  const endHold = () => {
    if (!holdRef.current) return;
    holdRef.current = false;
    if (stateRef.current !== "recording") return;
    clearTimers();
    finishCycle(sceneRef.current);
  };

  // Space bar = the hotkey (only while the section is on screen)
  useEffectP(() => {
    const down = (e) => {
      if (e.code !== "Space" || e.repeat) return;
      const tag = document.activeElement?.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA") return;
      const el = document.getElementById("pill");
      if (!el) return;
      const r = el.getBoundingClientRect();
      if (r.bottom < 0 || r.top > innerHeight) return;
      e.preventDefault();
      startHold();
    };
    const up = (e) => { if (e.code === "Space") endHold(); };
    window.addEventListener("keydown", down);
    window.addEventListener("keyup", up);
    return () => { window.removeEventListener("keydown", down); window.removeEventListener("keyup", up); };
  }, []);

  // start the reel
  useEffectP(() => {
    later(() => playScene(0), 1400);
    return clearTimers;
  }, []);

  const statusLine = {
    idle: "between missions",
    recording: `recording · dictating into ${scene.label}`,
    processing: "processing · slicing the fillers",
    result: "done · clean text at the cursor",
  }[state];

  return (
    <section className="section pill-section" id="pill">
      <div className="section-head">
        <div className="section-eyebrow">面 · THE FACE</div>
        <h2 className="section-title">This little guy lives above your Dock.</h2>
        <p className="section-sub">Same ninja you met up top, at <strong>actual size</strong>, working through a normal day: prompting Claude in the terminal, cleaning up a Slack message, sending an email. Hold <kbd>space</kbd> (or press and hold him) to run a mission yourself.</p>
      </div>

      <div className="pill-desktop">
        <div className="pill-menubar" aria-hidden>
          <span className="pill-menubar-apple">⌘</span>
          <span>Finder</span><span>File</span><span>Edit</span><span>View</span>
          <span className="pill-menubar-right">🥷 {new Date().toLocaleDateString("en-US", { weekday: "short" })} 9:41</span>
        </div>

        <div className="pill-status">{statusLine}</div>

        <PillAppWindow scene={scene} mode={winMode} text={winText} active={state === "recording" || state === "result"} />

        <div
          id="pill"
          className="pill-slot"
          onMouseDown={startHold}
          onMouseUp={endHold}
          onMouseLeave={() => { setHovering(false); endHold(); }}
          onMouseEnter={() => setHovering(true)}
          onTouchStart={(e) => { e.preventDefault(); startHold(); }}
          onTouchEnd={endHold}
          role="button"
          aria-label="Hold to simulate dictation"
        >
          <div className={`pill-tooltip ${hovering && state === "idle" ? "is-shown" : ""}`}>Fn</div>
          <NinjaPill state={state} level={level} phase={phase} hovering={hovering} scale={1} />
        </div>

        <div className="pill-dock" aria-hidden>
          {["📁", "🌐", "✉️", "📝", "🎵", "🖼️", "⚙️"].map((g, i) => (
            <span key={i} className="pill-dock-app">{g}</span>
          ))}
        </div>
      </div>

      <div className="pill-hint">
        {everHeld
          ? "That's the whole UI, at actual size. No window, no modal. It ends when you let go."
          : "Shown at actual size. Try it: hold space, pretend to talk, release."}
      </div>
    </section>
  );
}

Object.assign(window, { PillShowcase, NinjaPill });
