// app.jsx — root with katana cut transition + heavy tweaks

const { useTweaks, TweaksPanel, TweakSection, TweakSlider, TweakToggle, TweakRadio, TweakSelect, TweakText, TweakNumber, TweakColor } = window;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "displayFont": "Cormorant Garamond",
  "bodyFont": "Tenor Sans",
  "mood": "midnight",
  "ember": "#b73a2b",
  "auraIntensity": 0.65,
  "auraSize": 95,
  "auraBlur": 38,
  "brushStrokes": true,
  "brushIntensity": 0.55,
  "maskScale": 1.0,
  "maskOffsetX": 0,
  "maskOffsetY": 0,
  "maskRotation": 0,
  "maskBlend": "normal",
  "maskBrightness": 1.0,
  "maskParallax": 22,
  "maskBreath": true,
  "petalsOn": true,
  "petalCount": 22,
  "petalSpeed": 1.0,
  "petalBlur": 1.0,
  "kanjiOn": true,
  "vignette": 0.55,
  "noise": 0.6,
  "titleLine1": "A silence",
  "titleLine2": "observes the",
  "titleLine3": "city, calmly.",
  "leadText": "The mask does not speak. It listens to the rain on the metal awnings and the slow exhale of a vending machine — and somehow, the room becomes calmer."
}/*EDITMODE-END*/;

const MOOD_PRESETS = {
  midnight: { bg1: "oklch(0.07 0.005 30)", bg2: "oklch(0.10 0.008 30)", bg3: "oklch(0.08 0.006 28)", glow: 0.55 },
  crimson:  { bg1: "oklch(0.10 0.04 25)",  bg2: "oklch(0.13 0.07 24)",  bg3: "oklch(0.08 0.04 22)",  glow: 0.95 },
  ink:      { bg1: "oklch(0.04 0.002 30)", bg2: "oklch(0.06 0.003 30)", bg3: "oklch(0.03 0.002 28)", glow: 0.35 },
  dusk:     { bg1: "oklch(0.12 0.02 60)",  bg2: "oklch(0.15 0.03 40)",  bg3: "oklch(0.10 0.04 28)",  glow: 0.7 },
};

const PAGES = [
  { id: "home",    label: "Home",    idx: "01", Comp: () => <window.HomePage /> },
  { id: "stories", label: "Stories", idx: "02", Comp: () => <window.StoriesPage /> },
  { id: "about",   label: "About",   idx: "03", Comp: () => <window.AboutPage /> },
  { id: "contact", label: "Contact", idx: "04", Comp: () => <window.ContactPage /> },
];

function applyTweakVars(t) {
  const root = document.documentElement;
  const mood = MOOD_PRESETS[t.mood] || MOOD_PRESETS.midnight;
  root.style.setProperty('--display-font', `"${t.displayFont}", "Shippori Mincho", serif`);
  root.style.setProperty('--body-font', `"${t.bodyFont}", "Helvetica Neue", sans-serif`);
  root.style.setProperty('--ember', t.ember);
  root.style.setProperty('--bg1', mood.bg1);
  root.style.setProperty('--bg2', mood.bg2);
  root.style.setProperty('--bg3', mood.bg3);
  root.style.setProperty('--mood-glow', mood.glow);
  root.style.setProperty('--aura-intensity', t.auraIntensity);
  root.style.setProperty('--aura-size', `${t.auraSize}%`);
  root.style.setProperty('--aura-blur', `${t.auraBlur}px`);
  root.style.setProperty('--mask-scale', t.maskScale);
  root.style.setProperty('--mask-x', `${t.maskOffsetX}px`);
  root.style.setProperty('--mask-y', `${t.maskOffsetY}px`);
  root.style.setProperty('--mask-rot', `${t.maskRotation}deg`);
  root.style.setProperty('--mask-blend', t.maskBlend);
  root.style.setProperty('--mask-bright', t.maskBrightness);
  root.style.setProperty('--mask-parallax', t.maskParallax);
  root.style.setProperty('--vignette', t.vignette);
  root.style.setProperty('--noise', t.noise);
  root.style.setProperty('--brush-op', t.brushStrokes ? t.brushIntensity : 0);
  root.style.setProperty('--petal-blur', t.petalBlur);
  root.style.setProperty('--petal-speed', t.petalSpeed);
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [page, setPage] = React.useState("home");
  const [phase, setPhase] = React.useState("idle");
  const cutLayerRef = React.useRef(null);
  const upperRef = React.useRef(null);
  const lowerRef = React.useRef(null);
  const bladeRef = React.useRef(null);
  const sceneRef = React.useRef(null);

  React.useEffect(() => { applyTweakVars(t); }, [t]);

  // Generate a jagged "paper tear" edge along the diagonal between two halves.
  // Returns { upperClip, lowerClip } as CSS clip-path polygon strings.
  // Both share the same jagged sequence so they fit perfectly before separating.
  const generateTearEdge = React.useCallback(() => {
    const startY = 62;          // % at left edge
    const endY   = 38;          // % at right edge
    const segments = 64;        // jagged points
    const baseJitter = 1.6;     // perpendicular jitter (% of viewport) — micro fibers
    const burstJitter = 5.5;    // occasional big rip
    const fiberChance = 0.18;   // probability of a long fiber spike

    const pts = [];
    for (let i = 0; i <= segments; i++) {
      const t = i / segments;
      // Easing in/out so the tear is more violent in the middle
      const violence = 0.55 + 0.45 * Math.sin(Math.PI * t);
      const xBase = t * 100;
      const yBase = startY + (endY - startY) * t;
      // Perpendicular direction to the diagonal
      const dx = 100, dy = (endY - startY);
      const len = Math.hypot(dx, dy);
      const nx = -dy / len, ny = dx / len;
      // Random jitter: mostly small, occasionally a fiber-spike
      let jitter = (Math.random() - 0.5) * baseJitter * violence;
      if (Math.random() < fiberChance) {
        jitter += (Math.random() < 0.5 ? -1 : 1) * Math.random() * burstJitter * violence;
      }
      // Tiny horizontal nudge so points aren't perfectly evenly spaced
      const xJit = (Math.random() - 0.5) * 1.2;
      const x = xBase + nx * jitter + xJit;
      const y = yBase + ny * jitter;
      pts.push([x, y]);
    }

    const fwd = pts.map(([x, y]) => `${x.toFixed(2)}% ${y.toFixed(2)}%`).join(", ");
    const rev = pts.slice().reverse().map(([x, y]) => `${x.toFixed(2)}% ${y.toFixed(2)}%`).join(", ");
    // Upper: top-left -> top-right -> jagged edge from right back to left
    const upperClip = `polygon(0% 0%, 100% 0%, ${rev})`;
    // Lower: jagged edge from left to right -> bottom-right -> bottom-left
    const lowerClip = `polygon(${fwd}, 100% 100%, 0% 100%)`;
    return { upperClip, lowerClip };
  }, []);

  const go = React.useCallback((target) => {
    if (target === page || phase !== "idle") return;
    setPhase("cut");
    const upper = upperRef.current, lower = lowerRef.current, blade = bladeRef.current;
    if (!upper || !lower || !blade) return;

    // Fresh tear edge for every transition
    const { upperClip, lowerClip } = generateTearEdge();
    upper.style.clipPath = upperClip;
    lower.style.clipPath = lowerClip;

    // Match the blade angle to the actual on-screen diagonal of the tear.
    // Tear goes from (0, 62%H) to (100%W, 38%H). In screen px the angle depends on viewport ratio.
    const W = window.innerWidth, H = window.innerHeight;
    const dyPx = (38 - 62) / 100 * H;          // negative
    const angleRad = Math.atan2(dyPx, W);
    const angleDeg = angleRad * 180 / Math.PI;

    blade.style.transition = "none";
    blade.style.transform = `translate(-50%, -50%) rotate(${angleDeg.toFixed(2)}deg) scaleX(0)`;
    blade.style.opacity = "0";
    upper.style.transition = "none"; lower.style.transition = "none";
    upper.style.transform = "translate(0,0) rotate(0deg)";
    lower.style.transform = "translate(0,0) rotate(0deg)";
    upper.style.opacity = "0"; lower.style.opacity = "0";
    void cutLayerRef.current.offsetWidth;

    requestAnimationFrame(() => {
      blade.style.transition = "transform 380ms cubic-bezier(.18,.9,.2,1), opacity 200ms ease-out";
      blade.style.transform = `translate(-50%, -50%) rotate(${angleDeg.toFixed(2)}deg) scaleX(1)`;
      blade.style.opacity = "1";
    });
    setTimeout(() => {
      upper.style.transition = "opacity 110ms ease-out";
      lower.style.transition = "opacity 110ms ease-out";
      upper.style.opacity = "1"; lower.style.opacity = "1";
    }, 380);
    setTimeout(() => setPage(target), 500);
    setTimeout(() => {
      upper.style.transition = "transform 920ms cubic-bezier(.4,.05,.1,1), opacity 780ms ease-in 300ms";
      lower.style.transition = "transform 920ms cubic-bezier(.4,.05,.1,1), opacity 780ms ease-in 300ms";
      upper.style.transform = "translate(-3vw, -55vh) rotate(-2deg)";
      lower.style.transform = "translate(3vw, 55vh) rotate(-2deg)";
      upper.style.opacity = "0"; lower.style.opacity = "0";
      blade.style.transition = "opacity 520ms ease-out 200ms";
      blade.style.opacity = "0";
    }, 580);
    setTimeout(() => setPhase("idle"), 1500);
  }, [page, phase, generateTearEdge]);

  const Active = (PAGES.find((p) => p.id === page) || PAGES[0]).Comp;

  return (
    <>
      <div className="scene" ref={sceneRef} aria-hidden="true"></div>
      {t.petalsOn && <window.PetalField key={t.petalCount + ":" + t.petalSpeed + ":" + t.petalBlur} count={t.petalCount} speed={t.petalSpeed} blur={t.petalBlur} />}

      <div className="app">
        <nav className="nav">
          <div className="brand">
            <span className="mark">侍</span>
            <span className="sub">Ghost · Studio</span>
          </div>
          <div className="menu">
            {PAGES.map((p) => (
              <button
                key={p.id}
                className={p.id === page ? "active" : ""}
                onClick={() => go(p.id)}
                disabled={phase !== "idle"}
              >
                <span className="idx">{p.idx}</span>{p.label}
              </button>
            ))}
          </div>
        </nav>

        <main className="stage">
          <Active key={page} t={t} />
        </main>

        <footer className="rail">
          <div className="group">
            <span className="bone">SMR · Vol. III</span>
            <span>—</span>
            <span>Edition for the late hours</span>
          </div>
          <div className="scroll">scroll, slowly</div>
          <div className="group">
            <span className="bone">© MMXXVI</span>
          </div>
        </footer>
      </div>

      <div className="vignette" aria-hidden="true"></div>

      <div className={`cut-layer ${phase !== "idle" ? "active" : ""}`} ref={cutLayerRef} aria-hidden="true">
        <div className="half upper" ref={upperRef}></div>
        <div className="half lower" ref={lowerRef}></div>
        <div className="blade" ref={bladeRef}></div>
      </div>

      <TweaksPanel title="Tweaks">
        <TweakSection label="Typography" />
        <TweakSelect label="Display font" value={t.displayFont}
          options={["Cormorant Garamond", "Yuji Boku", "Yuji Mai", "Shippori Mincho", "Zen Kurenaido", "Reggae One"]}
          onChange={(v) => setTweak('displayFont', v)} />
        <TweakSelect label="Body font" value={t.bodyFont}
          options={["Tenor Sans", "Shippori Mincho", "Helvetica Neue"]}
          onChange={(v) => setTweak('bodyFont', v)} />
        <TweakText label="Title line 1" value={t.titleLine1} onChange={(v) => setTweak('titleLine1', v)} />
        <TweakText label="Title line 2" value={t.titleLine2} onChange={(v) => setTweak('titleLine2', v)} />
        <TweakText label="Title line 3" value={t.titleLine3} onChange={(v) => setTweak('titleLine3', v)} />
        <TweakToggle label="Kanji subtitle" value={t.kanjiOn} onChange={(v) => setTweak('kanjiOn', v)} />

        <TweakSection label="Atmosphere" />
        <TweakRadio label="Mood" value={t.mood}
          options={["midnight","crimson","ink","dusk"]}
          onChange={(v) => setTweak('mood', v)} />
        <TweakColor label="Ember" value={t.ember}
          options={["#b73a2b", "#8a1d1d", "#d65238", "#5b1010", "#c97a4a", "#3a0d0d"]}
          onChange={(v) => setTweak('ember', v)} />
        <TweakSlider label="Aura intensity" value={t.auraIntensity} min={0} max={2} step={0.05}
          onChange={(v) => setTweak('auraIntensity', v)} />
        <TweakSlider label="Aura size" value={t.auraSize} min={40} max={160} step={2} unit="%"
          onChange={(v) => setTweak('auraSize', v)} />
        <TweakSlider label="Aura blur" value={t.auraBlur} min={0} max={120} step={1} unit="px"
          onChange={(v) => setTweak('auraBlur', v)} />
        <TweakToggle label="Brush strokes" value={t.brushStrokes} onChange={(v) => setTweak('brushStrokes', v)} />
        <TweakSlider label="Brush intensity" value={t.brushIntensity} min={0} max={1} step={0.02}
          onChange={(v) => setTweak('brushIntensity', v)} />
        <TweakSlider label="Vignette" value={t.vignette} min={0} max={1} step={0.02}
          onChange={(v) => setTweak('vignette', v)} />
        <TweakSlider label="Film grain" value={t.noise} min={0} max={1} step={0.02}
          onChange={(v) => setTweak('noise', v)} />

        <TweakSection label="Mask" />
        <TweakSlider label="Scale" value={t.maskScale} min={0.4} max={2.0} step={0.02}
          onChange={(v) => setTweak('maskScale', v)} />
        <TweakSlider label="Offset X" value={t.maskOffsetX} min={-400} max={400} step={4} unit="px"
          onChange={(v) => setTweak('maskOffsetX', v)} />
        <TweakSlider label="Offset Y" value={t.maskOffsetY} min={-300} max={300} step={2} unit="px"
          onChange={(v) => setTweak('maskOffsetY', v)} />
        <TweakSlider label="Rotation" value={t.maskRotation} min={-20} max={20} step={1} unit="°"
          onChange={(v) => setTweak('maskRotation', v)} />
        <TweakSlider label="Brightness" value={t.maskBrightness} min={0.4} max={1.6} step={0.02}
          onChange={(v) => setTweak('maskBrightness', v)} />
        <TweakRadio label="Blend" value={t.maskBlend}
          options={["normal","screen","lighten"]}
          onChange={(v) => setTweak('maskBlend', v)} />
        <TweakToggle label="Breathing" value={t.maskBreath} onChange={(v) => setTweak('maskBreath', v)} />

        <TweakSection label="Petals" />
        <TweakToggle label="Enabled" value={t.petalsOn} onChange={(v) => setTweak('petalsOn', v)} />
        <TweakSlider label="Count" value={t.petalCount} min={0} max={60} step={1}
          onChange={(v) => setTweak('petalCount', v)} />
        <TweakSlider label="Speed" value={t.petalSpeed} min={0.3} max={2.5} step={0.05} unit="×"
          onChange={(v) => setTweak('petalSpeed', v)} />
        <TweakSlider label="Blur (depth)" value={t.petalBlur} min={0} max={2} step={0.05} unit="×"
          onChange={(v) => setTweak('petalBlur', v)} />
      </TweaksPanel>
    </>
  );
}

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

window.__currentTweaks = TWEAK_DEFAULTS;
window.addEventListener('message', (e) => {
  if (e.data && e.data.type === '__edit_mode_set_keys') {
    window.__currentTweaks = { ...window.__currentTweaks, ...e.data.edits };
  }
});

// fade-in observer
const observer = new MutationObserver(() => {
  document.querySelectorAll(".stage .page:not(.enter)").forEach((el) => {
    requestAnimationFrame(() => el.classList.add("enter"));
  });
});
observer.observe(document.getElementById("root"), { childList: true, subtree: true });

// breath-on-off class
const breathObserver = new MutationObserver(() => {
  // no-op placeholder
});
