// src/well.jsx — water graphics, ripples, scroll-fill engine

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

// ─── matchMedia hook ──────────────────────────────────────────────────
// Reads on first render so layouts don't flash the wrong breakpoint.
function useMediaQuery(query) {
  const [matches, setMatches] = useState(() => {
    if (typeof window === "undefined" || !window.matchMedia) return false;
    return window.matchMedia(query).matches;
  });
  useEffect(() => {
    if (!window.matchMedia) return;
    const mql = window.matchMedia(query);
    const onChange = (e) => setMatches(e.matches);
    setMatches(mql.matches);
    mql.addEventListener ? mql.addEventListener("change", onChange) : mql.addListener(onChange);
    return () => {
      mql.removeEventListener ? mql.removeEventListener("change", onChange) : mql.removeListener(onChange);
    };
  }, [query]);
  return matches;
}

// ─── scroll progress hook ─────────────────────────────────────────────
function useScrollFill(override) {
  const [fill, setFill] = useState(0);
  useEffect(() => {
    if (override >= 0) { setFill(override); return; }
    let raf = 0;
    const tick = () => {
      const max = document.documentElement.scrollHeight - window.innerHeight;
      const p = max > 0 ? window.scrollY / max : 0;
      setFill(Math.min(1, Math.max(0, p)));
      raf = 0;
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(tick); };
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    tick();
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [override]);
  return fill;
}

// ─── animated wave path (used in hero + depth meter) ──────────────────
function WavePath({ amp = 8, freq = 1.5, phase = 0, y = 50, w = 1440, h = 100, fill = "#0E3A52", motion = "medium" }) {
  const ref = useRef(null);
  useEffect(() => {
    if (motion === "none") return;
    const speed = motion === "heavy" ? 0.0012 : motion === "medium" ? 0.0006 : 0.0003;
    let raf;
    const start = performance.now();
    const loop = (t) => {
      const dt = (t - start) * speed;
      const el = ref.current;
      if (el) {
        const segs = 60;
        const pts = [];
        for (let i = 0; i <= segs; i++) {
          const x = (i / segs) * w;
          const yy = y + Math.sin((i / segs) * Math.PI * 2 * freq + phase + dt) * amp
                       + Math.sin((i / segs) * Math.PI * 2 * (freq * 1.7) + phase * 1.3 + dt * 0.7) * (amp * 0.4);
          pts.push(`${x.toFixed(1)},${yy.toFixed(1)}`);
        }
        el.setAttribute("d", `M0,${h} L0,${y} L${pts.join(" L")} L${w},${h} Z`);
      }
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [amp, freq, phase, y, w, h, motion]);
  return <path ref={ref} fill={fill} />;
}

// ─── caustic shimmer overlay ──────────────────────────────────────────
function Caustics({ opacity = 0.35, motion = "medium" }) {
  if (motion === "none") return null;
  const dur = motion === "heavy" ? "14s" : motion === "medium" ? "26s" : "44s";
  return (
    <svg className="caustics" width="100%" height="100%" preserveAspectRatio="none"
         style={{position:"absolute",inset:0,pointerEvents:"none",opacity,mixBlendMode:"soft-light"}}>
      <defs>
        <filter id="causticBlur" x="-10%" y="-10%" width="120%" height="120%">
          <feTurbulence type="fractalNoise" baseFrequency="0.012 0.018" numOctaves="2" seed="3">
            <animate attributeName="baseFrequency" dur={dur} values="0.012 0.018;0.018 0.012;0.012 0.018" repeatCount="indefinite" />
          </feTurbulence>
          <feDisplacementMap in="SourceGraphic" scale="22" />
          <feGaussianBlur stdDeviation="6" />
        </filter>
      </defs>
      <rect width="100%" height="100%" fill="rgba(255,255,255,.45)" filter="url(#causticBlur)" />
    </svg>
  );
}

// ─── water-fill background — fixed; rises with scroll ─────────────────
function WaterFill({ fill, palette, motion }) {
  // fill: 0..1 → water level rises from bottom
  const level = 100 - fill * 92; // %; leave a small dry strip even at full
  return (
    <div className="waterfill" aria-hidden style={{
      position:"fixed", inset:0, zIndex:0, pointerEvents:"none", overflow:"hidden"
    }}>
      {/* dry sand / sky on top */}
      <div style={{position:"absolute",inset:0,background:`linear-gradient(180deg, ${palette.bg} 0%, ${palette.surfaceWarm} 100%)`}} />
      {/* water body */}
      <div style={{
        position:"absolute", left:0, right:0, top:`${level}%`, bottom:0,
        background:`linear-gradient(180deg, ${palette.waterLight} 0%, ${palette.water} 60%, #03182A 100%)`,
        transition:"top .35s cubic-bezier(.22,.61,.36,1)",
      }}>
        {/* surface wave */}
        <svg className="surface" width="100%" height="60" preserveAspectRatio="none"
             viewBox="0 0 1440 60" style={{position:"absolute", top:-32, left:0, width:"100%", height:60}}>
          <WavePath amp={6} freq={2} phase={0}    y={28} w={1440} h={60} fill={palette.waterLight} motion={motion} />
          <WavePath amp={4} freq={3.2} phase={1.6} y={32} w={1440} h={60} fill={palette.water}      motion={motion} />
        </svg>
        <Caustics opacity={0.25} motion={motion} />
        {/* rising bubbles */}
        {motion !== "none" && <Bubbles motion={motion} />}
      </div>
    </div>
  );
}

function Bubbles({ motion, palette, count }) {
  const n = count != null ? count : (motion === "heavy" ? 28 : motion === "low" ? 10 : 18);
  const seeds = useMemo(() => Array.from({length: n}, (_, i) => ({
    x: Math.random() * 100,
    size: 3 + Math.random() * 9,
    dur: 14 + Math.random() * 22,
    delay: -Math.random() * 30,
    drift: (Math.random() - .5) * 60,
  })), [n]);
  const tint = palette && palette.isDark === false
    ? `radial-gradient(circle at 30% 30%, ${palette.water}cc, ${palette.water}22)`
    : "radial-gradient(circle at 30% 30%, rgba(255,255,255,.85), rgba(255,255,255,.12))";
  return (
    <div style={{position:"absolute",inset:0,pointerEvents:"none",overflow:"hidden"}}>
      <style>{`
        @keyframes bubrise { 0% { transform: translate(0,0); opacity:0 }
          10% { opacity:.55 } 90% { opacity:.55 }
          100% { transform: translate(var(--drift), -110vh); opacity:0 } }
      `}</style>
      {seeds.map((s, i) => (
        <span key={i} style={{
          position:"absolute", left:`${s.x}%`, bottom: -20,
          width: s.size, height: s.size, borderRadius:"50%",
          background: tint,
          filter:"blur(.4px)",
          ["--drift"]: `${s.drift}px`,
          animation: `bubrise ${s.dur}s linear ${s.delay}s infinite`,
        }} />
      ))}
    </div>
  );
}

// Full-viewport fixed bubble field — independent of the (now-removed) water fill
function BubbleField({ palette, motion }) {
  const reduced = useMediaQuery("(prefers-reduced-motion: reduce)");
  if (motion === "none" || reduced) return null;
  return (
    <div aria-hidden style={{
      position:"fixed", inset:0, zIndex:1, pointerEvents:"none", overflow:"hidden",
    }}>
      <Bubbles palette={palette} motion={motion} />
    </div>
  );
}

// ─── striped placeholder for imagery ──────────────────────────────────
function PhotoSlot({ label, w, h, palette, accent, style }) {
  return (
    <div style={{
      position:"relative", width:w, height:h,
      background: `repeating-linear-gradient(135deg, ${palette.surfaceWarm} 0 14px, ${palette.surface} 14px 28px)`,
      border: `1px solid ${palette.rim}`,
      borderRadius: 2,
      display:"flex", alignItems:"center", justifyContent:"center",
      color: palette.inkSoft,
      ...style,
    }}>
      <div className="mono" style={{fontSize:10, opacity:.7, textAlign:"center", padding:12}}>
        ○ &nbsp; {label}
      </div>
      {accent && <div style={{position:"absolute", top:8, left:8, width:6, height:6, borderRadius:"50%", background: accent}} />}
    </div>
  );
}

// ─── ripple-on-hover wrapper ──────────────────────────────────────────
function Ripple({ children, palette, motion }) {
  const ref = useRef(null);
  const burst = (e) => {
    if (motion === "none") return;
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const x = e.clientX - r.left, y = e.clientY - r.top;
    const sp = document.createElement("span");
    sp.className = "rip";
    sp.style.cssText = `position:absolute;left:${x}px;top:${y}px;width:8px;height:8px;border-radius:50%;
      background:${palette.accent};opacity:.5;transform:translate(-50%,-50%);
      pointer-events:none;animation:rip 900ms ease-out forwards;mix-blend-mode:${palette.isDark?"screen":"multiply"}`;
    el.appendChild(sp);
    setTimeout(() => sp.remove(), 950);
  };
  return (
    <div ref={ref} onMouseEnter={burst} style={{position:"relative", overflow:"hidden"}}>
      <style>{`@keyframes rip { 0%{width:8px;height:8px;opacity:.5} 100%{width:600px;height:600px;opacity:0} }`}</style>
      {children}
    </div>
  );
}

Object.assign(window, { useScrollFill, useMediaQuery, WavePath, Caustics, WaterFill, PhotoSlot, Ripple, BubbleField });
