// wind.jsx — wind canvas (particles + cursor trail), wind HUD, custom cursor, kite floater

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

// ─── Particle field (canvas) ─────────────────────────────────────
function useWindCanvas(intensity = 0.6, cursorTrail = true) {
  const stateRef = useRef({
    particles: [],
    cursor: { x: -100, y: -100, lastX: -100, lastY: -100, active: false },
    raf: 0,
    intensity,
    cursorTrail
  });

  // keep latest values in ref without restarting RAF
  useEffect(() => {
    stateRef.current.intensity = intensity;
    stateRef.current.cursorTrail = cursorTrail;
  }, [intensity, cursorTrail]);

  useEffect(() => {
    const canvas = document.getElementById("wind-canvas");
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    const st = stateRef.current;

    let W = 0,H = 0,DPR = Math.min(window.devicePixelRatio || 1, 2);

    const resize = () => {
      W = window.innerWidth;
      H = window.innerHeight;
      canvas.width = W * DPR;
      canvas.height = H * DPR;
      canvas.style.width = W + "px";
      canvas.style.height = H + "px";
      ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
    };
    resize();
    window.addEventListener("resize", resize);

    // particles — long streaks blown by the wind
    const baseCount = 80;
    const seed = () => {
      st.particles = [];
      const count = Math.floor(baseCount * st.intensity + 10);
      for (let i = 0; i < count; i++) {
        st.particles.push({
          x: Math.random() * W,
          y: Math.random() * H,
          vx: 0.7 + Math.random() * 1.6,
          len: 22 + Math.random() * 90,
          op: 0.05 + Math.random() * 0.25,
          life: Math.random() * 600,
          maxLife: 600 + Math.random() * 400
        });
      }
    };
    seed();
    window.addEventListener("resize", seed);

    // cursor trail particles
    const trail = [];
    const pushTrail = (x, y) => {
      if (!st.cursorTrail) return;
      const dx = x - st.cursor.lastX;
      const dy = y - st.cursor.lastY;
      const d = Math.hypot(dx, dy);
      // emit proportional to speed
      const count = Math.min(6, Math.floor(d / 6));
      for (let i = 0; i < count; i++) {
        const t = i / count;
        trail.push({
          x: st.cursor.lastX + dx * t + (Math.random() - 0.5) * 4,
          y: st.cursor.lastY + dy * t + (Math.random() - 0.5) * 4,
          vx: 0.4 + Math.random() * 1.2,
          vy: (Math.random() - 0.5) * 0.4,
          life: 0,
          maxLife: 60 + Math.random() * 60,
          op: 0.4 + Math.random() * 0.4
        });
      }
      st.cursor.lastX = x;
      st.cursor.lastY = y;
    };

    const onMove = (e) => {
      st.cursor.x = e.clientX;
      st.cursor.y = e.clientY;
      st.cursor.active = true;
      pushTrail(e.clientX, e.clientY);
    };
    const onLeave = () => {st.cursor.active = false;};
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseleave", onLeave);

    // colour pulled from CSS var
    const particleColour = () =>
    getComputedStyle(document.documentElement).getPropertyValue("--particle").trim() ||
    "rgba(255,255,255,0.5)";

    let t0 = performance.now();
    const tick = (t) => {
      const dt = Math.min(60, t - t0);
      t0 = t;
      ctx.clearRect(0, 0, W, H);

      const col = particleColour();

      // ambient streaks
      for (const p of st.particles) {
        p.x += p.vx * (dt / 16);
        p.life += dt;
        if (p.x > W + p.len || p.life > p.maxLife) {
          p.x = -p.len - Math.random() * 200;
          p.y = Math.random() * H;
          p.life = 0;
          p.vx = 0.7 + Math.random() * 1.6;
        }
        ctx.strokeStyle = col.replace(/,\s*[\d.]+\)/, `,${p.op * st.intensity})`);
        ctx.lineWidth = 0.6;
        ctx.beginPath();
        ctx.moveTo(p.x, p.y);
        ctx.lineTo(p.x + p.len, p.y);
        ctx.stroke();
      }

      // cursor trail
      for (let i = trail.length - 1; i >= 0; i--) {
        const p = trail[i];
        p.x += p.vx * (dt / 16);
        p.y += p.vy * (dt / 16);
        p.life += dt;
        const k = 1 - p.life / p.maxLife;
        if (k <= 0) {trail.splice(i, 1);continue;}
        ctx.strokeStyle = col.replace(/,\s*[\d.]+\)/, `,${p.op * k})`);
        ctx.lineWidth = 0.8;
        ctx.beginPath();
        ctx.moveTo(p.x, p.y);
        ctx.lineTo(p.x + 14 * k, p.y);
        ctx.stroke();
      }

      st.raf = requestAnimationFrame(tick);
    };
    st.raf = requestAnimationFrame(tick);

    return () => {
      cancelAnimationFrame(st.raf);
      window.removeEventListener("resize", resize);
      window.removeEventListener("resize", seed);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseleave", onLeave);
      ctx.clearRect(0, 0, W, H);
    };
  }, []);
}

// ─── Custom cursor (dot, scales on hover) ────────────────────────
function CustomCursor() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    let x = -50,y = -50,tx = -50,ty = -50;
    const move = (e) => {tx = e.clientX;ty = e.clientY;};
    window.addEventListener("mousemove", move);

    // hover detection on interactive elements
    const setHover = (on) => el && el.classList.toggle("hover", on);
    const interactiveSel = "a,button,input,textarea,select,[data-hover]";
    const onOver = (e) => {if (e.target.closest(interactiveSel)) setHover(true);};
    const onOut = (e) => {if (e.target.closest(interactiveSel)) setHover(false);};
    document.addEventListener("mouseover", onOver);
    document.addEventListener("mouseout", onOut);

    let raf = 0;
    const loop = () => {
      x += (tx - x) * 0.25;
      y += (ty - y) * 0.25;
      if (el) el.style.transform = `translate(${x}px,${y}px) translate(-50%,-50%)`;
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("mousemove", move);
      document.removeEventListener("mouseover", onOver);
      document.removeEventListener("mouseout", onOut);
    };
  }, []);
  return <div ref={ref} className="cursor" aria-hidden="true" />;
}

// ─── Live wind HUD ───────────────────────────────────────────────
function WindHUD({ visible = true }) {
  const [knots, setKnots] = useState(19);
  const [dir, setDir] = useState(85); // degrees, ENE-ish (side-shore)
  const [gust, setGust] = useState(23);

  useEffect(() => {
    let n = 19,d = 85,g = 23;
    const id = setInterval(() => {
      n = Math.max(12, Math.min(28, n + (Math.random() - 0.5) * 2));
      d = d + (Math.random() - 0.5) * 4;
      g = Math.max(n + 2, Math.min(34, n + 3 + Math.random() * 3));
      setKnots(+n.toFixed(0));
      setDir(+((d % 360 + 360) % 360).toFixed(0));
      setGust(+g.toFixed(0));
    }, 1800);
    return () => clearInterval(id);
  }, []);

  if (!visible) return null;
  const compass = (deg) => {
    const dirs = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];
    return dirs[Math.round(deg / 45) % 8];
  };

  return (
    <div className="wind-hud" aria-hidden="true">
      <div className="hud-h">
        <span className="live-dot" />
        <span>LIVE · GUAJIRÚ</span>
      </div>
      <div className="knots">{knots}<span>kt</span></div>
      <div className="row"><span>DIR</span><span>{compass(dir)} · {dir}°</span></div>
      <div className="row"><span>GUST</span><span>{gust} kt</span></div>
      <div className="row">
        <span>VANE</span>
        <span>
          <svg className="arrow" viewBox="0 0 14 14" style={{ transform: `rotate(${dir}deg)` }}>
            <path d="M7 1 L10 9 L7 7.5 L4 9 Z" fill="currentColor" />
          </svg>
        </span>
      </div>
    </div>);

}

// ─── Kite SVG floater that drifts across screen ──────────────────
function KiteFloater({ on = true }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!on) return;
    const el = ref.current;
    if (!el) return;
    let raf = 0;
    let t0 = performance.now();
    const loop = (t) => {
      const dt = (t - t0) / 1000;
      // figure-8 path across upper portion of screen
      const W = window.innerWidth,H = window.innerHeight;
      const cx = W * 0.5 + Math.sin(dt * 0.18) * W * 0.45;
      const cy = H * 0.18 + Math.sin(dt * 0.36) * H * 0.06;
      const rot = Math.sin(dt * 0.3) * 18 - 12;
      el.style.transform = `translate(${cx}px,${cy}px) rotate(${rot}deg)`;
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [on]);

  return (
    <div ref={ref} className={"kite-floater " + (on ? "on" : "")} aria-hidden="true">
      <svg viewBox="0 0 60 60" width="36" height="36">
        {/* foil kite arc */}
        <path d="M5 22 Q30 4 55 22 Q42 26 30 25 Q18 26 5 22 Z"
        fill="none" stroke="var(--accent)" strokeWidth="1.2" opacity="0.9" />
        <path d="M5 22 Q30 4 55 22" fill="none" stroke="var(--fg)" strokeWidth=".6" opacity=".4" />
        {/* lines */}
        <line x1="14" y1="24" x2="28" y2="56" stroke="var(--fg-soft)" strokeWidth=".5" opacity=".55" />
        <line x1="46" y1="24" x2="32" y2="56" stroke="var(--fg-soft)" strokeWidth=".5" opacity=".55" />
        {/* rider speck */}
        <circle cx="30" cy="57" r="1.2" fill="var(--accent)" />
      </svg>
    </div>);

}

// ─── Wind-driven title (chars sway, words don't break) ──────────
function WindyText({ text, intensity = 0.6 }) {
  // split into words so they don't break mid-letter
  const tokens = useMemo(() => {
    // returns array of {word: string, trailingSpace: bool}
    const out = [];
    const re = /(\S+)(\s*)/g;
    let m;
    while ((m = re.exec(text)) !== null) {
      out.push({ word: m[1], space: m[2] });
    }
    return out;
  }, [text]);

  const refs = useRef([]);
  useEffect(() => {
    let raf = 0;
    let t0 = performance.now();
    const loop = (t) => {
      const dt = (t - t0) / 1000;
      refs.current.forEach((el, i) => {
        if (!el) return;
        const phase = i * 0.22;
        const sway = Math.sin(dt * 1.4 + phase) * 2.4 * intensity +
        Math.sin(dt * 0.55 + phase * 1.7) * 1.2 * intensity;
        const lift = Math.cos(dt * 0.9 + phase) * 1.4 * intensity;
        el.style.transform = `translate(${sway}px, ${lift}px)`;
      });
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [text, intensity]);

  let charIdx = 0;
  return (
    <span aria-label={text}>
      {tokens.map((tok, w) =>
      <React.Fragment key={w}>
          <span className="word" style={{ display: "inline-block", whiteSpace: "nowrap" }}>
            {Array.from(tok.word).map((c, i) => {
            const idx = charIdx++;
            return (
              <span
                key={i}
                ref={(el) => refs.current[idx] = el}
                className="char"
                style={{ display: "inline-block", color: "rgb(255, 179, 0)" }}>
                
                  {c}
                </span>);

          })}
          </span>
          {tok.space && <span style={{ whiteSpace: "pre" }}>{tok.space}</span>}
        </React.Fragment>
      )}
    </span>);

}

// ─── Reveal on scroll ────────────────────────────────────────────
function useReveal() {
  useEffect(() => {
    const reveal = (el) => el.classList.add("in");
    const els = () => document.querySelectorAll(".reveal:not(.in)");

    // immediate first pass — anything already on screen reveals right away
    const checkVisible = () => {
      const vh = window.innerHeight;
      els().forEach((el) => {
        const r = el.getBoundingClientRect();
        if (r.top < vh * 0.95 && r.bottom > 0) reveal(el);
      });
    };
    checkVisible();

    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            reveal(e.target);
            io.unobserve(e.target);
          }
        });
      },
      { threshold: 0.05, rootMargin: "0px 0px -4% 0px" }
    );
    els().forEach((el) => io.observe(el));

    // safety net — anything still hidden after 1.2s pops in
    const safety = setTimeout(() => {
      els().forEach(reveal);
    }, 1200);

    return () => {
      io.disconnect();
      clearTimeout(safety);
    };
  }, []);
}

// ─── Sticky-nav scrolled state ───────────────────────────────────
function useNavScroll() {
  useEffect(() => {
    const nav = document.querySelector(".nav");
    if (!nav) return;
    const onScroll = () => nav.classList.toggle("scrolled", window.scrollY > 60);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
}

Object.assign(window, {
  useWindCanvas, CustomCursor, WindHUD, KiteFloater, WindyText, useReveal, useNavScroll
});