/* global React */
const { useState, useEffect, useRef, useMemo, useCallback } = React;
const L = window.LH;

/* ---------- hooks ---------- */
function useLang() {
  const [lang, setLang] = useState(() => localStorage.getItem("lh_lang") || "es");
  useEffect(() => {
    localStorage.setItem("lh_lang", lang);
    document.documentElement.lang = lang;
  }, [lang]);
  return [lang, setLang];
}

function useTheme() {
  const [theme, setTheme] = useState(() => localStorage.getItem("lh_theme") || "dark");
  useEffect(() => {
    document.documentElement.dataset.theme = theme;
    localStorage.setItem("lh_theme", theme);
  }, [theme]);
  return [theme, setTheme];
}

function useReveal() {
  useEffect(() => {
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } });
    }, { threshold: 0.1, rootMargin: "0px 0px -5% 0px" });
    const scan = () => document.querySelectorAll("[data-rv]:not(.in)").forEach((el) => io.observe(el));
    scan();
    const mo = new MutationObserver(scan);
    mo.observe(document.body, { childList: true, subtree: true });
    return () => { io.disconnect(); mo.disconnect(); };
  }, []);
}

function useScrollY() {
  const [y, setY] = useState(0);
  useEffect(() => {
    let raf = null;
    const fn = () => { if (raf) return; raf = requestAnimationFrame(() => { setY(window.scrollY); raf = null; }); };
    fn();
    window.addEventListener("scroll", fn, { passive: true });
    return () => { window.removeEventListener("scroll", fn); if (raf) cancelAnimationFrame(raf); };
  }, []);
  return y;
}

function useSeen(threshold = 0.3) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    if (!ref.current || seen) return;
    const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setSeen(true); io.disconnect(); } }, { threshold });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [seen, threshold]);
  return [ref, seen];
}

function useCountUp(target, on, dur = 1600) {
  const [n, setN] = useState(0);
  useEffect(() => {
    if (!on) return;
    const t0 = performance.now();
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - t0) / dur);
      setN(Math.round((1 - Math.pow(1 - p, 4)) * target));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, on, dur]);
  return n;
}

function useCountdown(iso) {
  const calc = () => {
    const ms = new Date(iso).getTime() - Date.now();
    if (ms <= 0) return { d: 0, h: 0, m: 0, s: 0 };
    return { d: Math.floor(ms / 864e5), h: Math.floor(ms / 36e5) % 24, m: Math.floor(ms / 6e4) % 60, s: Math.floor(ms / 1e3) % 60 };
  };
  const [t, setT] = useState(calc);
  useEffect(() => {
    const id = setInterval(() => setT(calc()), 1000);
    return () => clearInterval(id);
  }, [iso]);
  return t;
}

/* ---------- 3D tilt ---------- */
function useTilt(max = 9) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    if (window.matchMedia("(hover: none)").matches) return;
    const inner = el.querySelector(".tilt-in");
    let raf = null, tx = 0, ty = 0, sh = 0;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const px = (e.clientX - r.left) / r.width - 0.5;
      const py = (e.clientY - r.top) / r.height - 0.5;
      tx = -py * max * 2; ty = px * max * 2; sh = 1;
      if (!raf) raf = requestAnimationFrame(apply);
    };
    const onLeave = () => { tx = 0; ty = 0; sh = 0; if (!raf) raf = requestAnimationFrame(apply); };
    const apply = () => {
      raf = null;
      inner.style.transform = `rotateX(${tx.toFixed(2)}deg) rotateY(${ty.toFixed(2)}deg)`;
      inner.style.setProperty("--sh", sh);
    };
    // wide hit area so the card reacts before the cursor reaches it
    window.addEventListener("mousemove", onMove, { passive: true });
    window.addEventListener("mouseout", onLeave);
    return () => { window.removeEventListener("mousemove", onMove); window.removeEventListener("mouseout", onLeave); if (raf) cancelAnimationFrame(raf); };
  }, [max]);
  return ref;
}

/* ---------- ripple / water canvas ---------- */
function RippleCanvas() {
  const ref = useRef(null);
  useEffect(() => {
    const c = ref.current; if (!c) return;
    const ctx = c.getContext("2d");
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    let w = 0, h = 0, raf, alive = true, t = 0;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const size = () => { w = c.clientWidth; h = c.clientHeight; c.width = w * dpr; c.height = h * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); };
    size();
    const ro = new ResizeObserver(size); ro.observe(c);
    const rings = [];
    let lastRing = 0;
    const onMove = (e) => {
      const r = c.getBoundingClientRect();
      if (e.clientY < r.top || e.clientY > r.bottom) return;
      const now = performance.now();
      if (now - lastRing < 190) return;
      lastRing = now;
      rings.push({ x: e.clientX - r.left, y: e.clientY - r.top, r: 0, a: 0.45 });
      if (rings.length > 14) rings.shift();
    };
    window.addEventListener("mousemove", onMove, { passive: true });

    const draw = () => {
      if (!alive) return;
      ctx.clearRect(0, 0, w, h);
      // horizontal wave bands
      for (let i = 0; i < 5; i++) {
        const yb = h * (0.52 + i * 0.11);
        const amp = 9 + i * 7;
        const fr = 0.0042 + i * 0.0013;
        const ph = t * (0.00055 + i * 0.00028);
        ctx.beginPath();
        ctx.moveTo(0, h);
        for (let x = 0; x <= w; x += 5) {
          const y = yb + Math.sin(x * fr + ph + i) * amp + Math.cos(x * fr * 2.1 + ph * 1.5) * amp * 0.38;
          ctx.lineTo(x, y);
        }
        ctx.lineTo(w, h); ctx.closePath();
        ctx.fillStyle = `rgba(0,102,255,${0.028 + i * 0.016})`;
        ctx.fill();
        ctx.strokeStyle = `rgba(127,179,255,${0.09 + i * 0.05})`;
        ctx.lineWidth = 1; ctx.stroke();
      }
      // pointer ripples
      for (let i = rings.length - 1; i >= 0; i--) {
        const rg = rings[i];
        rg.r += 2.6; rg.a *= 0.972;
        if (rg.a < 0.012) { rings.splice(i, 1); continue; }
        ctx.beginPath();
        ctx.arc(rg.x, rg.y, rg.r, 0, Math.PI * 2);
        ctx.strokeStyle = `rgba(255,107,53,${rg.a})`;
        ctx.lineWidth = 1.1; ctx.stroke();
        ctx.beginPath();
        ctx.arc(rg.x, rg.y, rg.r * 0.62, 0, Math.PI * 2);
        ctx.strokeStyle = `rgba(127,179,255,${rg.a * 0.6})`;
        ctx.lineWidth = 0.8; ctx.stroke();
      }
      t += 16;
      raf = requestAnimationFrame(draw);
    };
    draw();
    return () => { alive = false; cancelAnimationFrame(raf); ro.disconnect(); window.removeEventListener("mousemove", onMove); };
  }, []);
  return <canvas ref={ref} className="ripple" aria-hidden="true" />;
}

/* ---------- shared bits ---------- */
function Rise({ text, delay = 0, className = "" }) {
  return <span className={`w ${className}`}><i style={{ "--sd": `${delay}ms` }}>{text}</i></span>;
}

function SHead({ n, title, sub, lead, id }) {
  return (
    <div className="sh" data-rv id={id}>
      <div className="sh-r">
        <span className="num-tag">/{n}</span>
        <h2>{title}</h2>
      </div>
      {sub ? <div className="sh-sub">{sub}</div> : null}
      {lead ? <p className="sh-lead">{lead}</p> : null}
    </div>
  );
}

function CD({ iso, lbl, t }) {
  const c = useCountdown(iso);
  const u = [[c.d, t.days], [c.h, t.hrs], [c.m, t.min], [c.s, t.seg]];
  return (
    <div className="upc-cd">
      <div className="upc-cd-l">{lbl}</div>
      <div className="cdrow">
        {u.map(([v, k], i) => <div className="cdu" key={i}><b>{String(v).padStart(2, "0")}</b><i>{k}</i></div>)}
      </div>
    </div>
  );
}

/* ---------- icons ---------- */
const sw = { fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round" };
const ICONS = {
  flag: <svg viewBox="0 0 24 24" {...sw}><path d="M5 21V4M5 4c4-2 8 2 12 0v9c-4 2-8-2-12 0" /></svg>,
  medal: <svg viewBox="0 0 24 24" {...sw}><circle cx="12" cy="15" r="6" /><path d="M9 3l3 6 3-6" /></svg>,
  cup: <svg viewBox="0 0 24 24" {...sw}><path d="M7 4h10v5a5 5 0 01-10 0zM7 6H4v2a3 3 0 003 3M17 6h3v2a3 3 0 01-3 3M9 20h6M12 14v6" /></svg>,
  clock: <svg viewBox="0 0 24 24" {...sw}><circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 2" /></svg>,
  rings: <svg viewBox="0 0 24 24" {...sw}><circle cx="8" cy="10" r="4.5" /><circle cx="16" cy="10" r="4.5" /><circle cx="12" cy="16" r="4.5" /></svg>,
  reach: <svg viewBox="0 0 24 24" {...sw}><rect x="6" y="2.5" width="12" height="19" rx="2.5" /><path d="M10.5 5.5h3" /><path d="M12 17.5h.01" /></svg>,
  trophy: <svg viewBox="0 0 24 24" {...sw}><path d="M7 4h10v5a5 5 0 01-10 0zM7 6H4v2a3 3 0 003 3M17 6h3v2a3 3 0 01-3 3M9 20h6M12 14v6" /></svg>,
  globe: <svg viewBox="0 0 24 24" {...sw}><circle cx="12" cy="12" r="9" /><path d="M3 12h18M12 3c2.5 2.6 2.5 15.4 0 18M12 3c-2.5 2.6-2.5 15.4 0 18" /></svg>,
  ig: <svg viewBox="0 0 24 24" {...sw}><rect x="3" y="3" width="18" height="18" rx="5" /><circle cx="12" cy="12" r="4" /><circle cx="17.2" cy="6.8" r=".9" fill="currentColor" stroke="none" /></svg>,
  tiktok: <svg viewBox="0 0 24 24" {...sw}><path d="M14 3v10.5a3.5 3.5 0 11-3.5-3.5" /><path d="M14 6.2A4.8 4.8 0 0018.8 9" /></svg>,
  mail: <svg viewBox="0 0 24 24" {...sw}><rect x="2.5" y="5" width="19" height="14" rx="2" /><path d="M3 7l9 6 9-6" /></svg>,
  phone: <svg viewBox="0 0 24 24" {...sw}><path d="M5 3h3l2 5-2.2 1.4a12 12 0 005.8 5.8L15 13l5 2v3a2 2 0 01-2.2 2A16 16 0 013 5.2 2 2 0 015 3z" /></svg>,
  wa: <svg viewBox="0 0 24 24" {...sw}><path d="M12 21a9 9 0 10-7.8-4.5L3 21l4.6-1.2A9 9 0 0012 21z" /><path d="M9 9.5c0 3 2.5 5.5 5.5 5.5.6 0 1-.5 1-1l-1.4-.7-1 .8a5.6 5.6 0 01-2.2-2.2l.8-1L11 9.5c0-.5-.4-1-1-1s-1 .5-1 1z" /></svg>
};

const KPI_ICONS = ["flag", "medal", "cup", "clock", "rings"];

Object.assign(window, {
  L, useLang, useTheme, useReveal, useScrollY, useSeen, useCountUp, useCountdown,
  useTilt, RippleCanvas, Rise, SHead, CD, ICONS, KPI_ICONS
});
