/* global React, window, IntersectionObserver */
const { useState, useEffect, useRef, createContext, useContext, useCallback, useLayoutEffect } = React;

/* =========================================================
   Language context — exposes { L, lang, setLang, dir, t() }
   ========================================================= */
const LangContext = createContext(null);

function LangProvider({ children }) {
  const [lang, setLang] = useState(() => {
    try { return localStorage.getItem("mahara_lang") || "en"; }
    catch { return "en"; }
  });
  useEffect(() => {
    try { localStorage.setItem("mahara_lang", lang); } catch {}
    const dir = window.I18N[lang].dir;
    document.documentElement.setAttribute("lang", lang);
    document.documentElement.setAttribute("dir", dir);
  }, [lang]);
  const L = window.I18N[lang];
  const value = { lang, setLang, L, dir: L.dir };
  return React.createElement(LangContext.Provider, { value }, children);
}
function useLang() { return useContext(LangContext); }

/* =========================================================
   prefers-reduced-motion
   ========================================================= */
function useReducedMotion() {
  const [reduced, setReduced] = useState(() => {
    if (typeof window === "undefined") return false;
    return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  });
  useEffect(() => {
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    const onChange = e => setReduced(e.matches);
    mq.addEventListener("change", onChange);
    return () => mq.removeEventListener("change", onChange);
  }, []);
  return reduced;
}

/* =========================================================
   Reveal — fade + 16px translate-y on viewport enter
   Stagger children by index * delay
   ========================================================= */
function Reveal({ as: Tag = "div", delay = 0, className = "", style = {}, children, ...rest }) {
  const ref = useRef(null);
  const reduced = useReducedMotion();
  useEffect(() => {
    if (!ref.current) return;
    if (reduced) {
      ref.current.classList.add("in");
      return;
    }
    const io = new IntersectionObserver(
      entries => entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add("in");
          io.unobserve(e.target);
        }
      }),
      { threshold: 0.15, rootMargin: "-40px 0px -40px 0px" }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, [reduced]);
  return React.createElement(
    Tag,
    {
      ref,
      className: `reveal ${className}`.trim(),
      style: { "--rd": `${delay}ms`, ...style },
      ...rest,
    },
    children
  );
}

/* =========================================================
   DividerLine — animated gradient line that draws on enter
   ========================================================= */
function DividerLine() {
  const ref = useRef(null);
  const reduced = useReducedMotion();
  useEffect(() => {
    if (!ref.current) return;
    if (reduced) { ref.current.classList.add("in"); return; }
    const io = new IntersectionObserver(es => es.forEach(e => {
      if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); }
    }), { threshold: 0.5 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [reduced]);
  return <div className="divider-line" ref={ref} />;
}

/* =========================================================
   Magnetic wrapper — pulls toward cursor when within 60px
   ========================================================= */
function Magnetic({ children, strength = 0.32 }) {
  const ref = useRef(null);
  const reduced = useReducedMotion();
  useEffect(() => {
    if (reduced) return;
    const el = ref.current;
    if (!el) return;
    let rafId = null;
    let tx = 0, ty = 0;
    const onMove = e => {
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width / 2;
      const cy = r.top + r.height / 2;
      const dx = e.clientX - cx;
      const dy = e.clientY - cy;
      const dist = Math.hypot(dx, dy);
      const inRange = dist < 80;
      const f = inRange ? strength : 0;
      tx = dx * f;
      ty = dy * f;
      if (rafId) cancelAnimationFrame(rafId);
      rafId = requestAnimationFrame(() => {
        el.style.transform = `translate(${tx}px, ${ty}px)`;
      });
    };
    const onLeave = () => {
      el.style.transform = "translate(0,0)";
    };
    window.addEventListener("mousemove", onMove);
    el.addEventListener("mouseleave", onLeave);
    return () => {
      window.removeEventListener("mousemove", onMove);
      el.removeEventListener("mouseleave", onLeave);
    };
  }, [reduced, strength]);
  return React.createElement("span", { ref, className: "magnetic", style: { display: "inline-block" } }, children);
}

/* =========================================================
   useTilt — mouse-tilt on cards, max 6deg
   ========================================================= */
function useTilt(maxDeg = 6) {
  const ref = useRef(null);
  const reduced = useReducedMotion();
  useEffect(() => {
    if (reduced) return;
    const el = ref.current;
    if (!el) return;
    let raf = null;
    const onMove = e => {
      const r = el.getBoundingClientRect();
      const px = (e.clientX - r.left) / r.width;
      const py = (e.clientY - r.top) / r.height;
      const rx = (0.5 - py) * maxDeg * 2;
      const ry = (px - 0.5) * maxDeg * 2;
      if (raf) cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.transform = `perspective(900px) rotateX(${rx}deg) rotateY(${ry}deg)`;
      });
    };
    const onLeave = () => { el.style.transform = "perspective(900px) rotateX(0) rotateY(0)"; };
    el.addEventListener("mousemove", onMove);
    el.addEventListener("mouseleave", onLeave);
    return () => {
      el.removeEventListener("mousemove", onMove);
      el.removeEventListener("mouseleave", onLeave);
    };
  }, [maxDeg, reduced]);
  return ref;
}

/* =========================================================
   CountUp — animate from 0 → value on viewport enter.
   Renders the number digit-by-digit; preserves currency / suffix.
   ========================================================= */
function CountUp({ value, suffix = "", className = "", duration = 1400 }) {
  const ref = useRef(null);
  const reduced = useReducedMotion();
  const [display, setDisplay] = useState(reduced ? value : null);

  useEffect(() => {
    if (reduced) { setDisplay(value); return; }
    const el = ref.current;
    if (!el) return;

    // Parse: e.g. "$1M", "100", "20"
    const match = value.match(/^(\D*)([\d,.]+)(\D*)$/);
    if (!match) { setDisplay(value); return; }
    const [, prefix, numStr, tail] = match;
    const isMillion = tail === "M" || tail === "m" || numStr.indexOf(".") !== -1;
    const target = parseFloat(numStr.replace(/,/g, ""));
    if (!isFinite(target)) { setDisplay(value); return; }

    const io = new IntersectionObserver(es => es.forEach(e => {
      if (!e.isIntersecting) return;
      io.unobserve(e.target);
      const start = performance.now();
      const tick = (now) => {
        const t = Math.min(1, (now - start) / duration);
        const eased = 1 - Math.pow(1 - t, 3); // easeOutCubic
        const cur = target * eased;
        let label;
        if (target >= 1000) label = Math.round(cur).toLocaleString();
        else if (numStr.indexOf(".") !== -1) label = cur.toFixed(1);
        else label = Math.round(cur).toString();
        setDisplay(`${prefix}${label}${tail}`);
        if (t < 1) requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick);
    }), { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [value, duration, reduced]);

  return (
    <span ref={ref} className={className}>
      {display ?? value}
      {suffix ? <span className="suffix">{suffix}</span> : null}
    </span>
  );
}

/* =========================================================
   Lucide icon — fetch SVG path data inline, no CDN dep.
   ========================================================= */
const ICONS = {
  "play": `<polygon points="6 3 20 12 6 21 6 3"/>`,
  "x": `<path d="M18 6 6 18"/><path d="m6 6 12 12"/>`,
  "arrow-right": `<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>`,
  "arrow-left": `<path d="M19 12H5"/><path d="m12 19-7-7 7-7"/>`,
  "chevron-down": `<path d="m6 9 6 6 6-6"/>`,
  "plus": `<path d="M12 5v14"/><path d="M5 12h14"/>`,
  "check": `<path d="M20 6 9 17l-5-5"/>`,
  "x-mark": `<path d="M18 6 6 18"/><path d="m6 6 12 12"/>`,
  "globe": `<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>`,
  "lock": `<rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>`,
  "shield-check": `<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/>`,
  "calendar": `<rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4"/><path d="M8 2v4"/><path d="M3 10h18"/>`,
  "trending-up": `<polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/><polyline points="16 7 22 7 22 13"/>`,
  "wallet": `<path d="M19 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-2"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4Z"/>`,
  "graduation-cap": `<path d="M22 10v6"/><path d="M6 12.5V16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-3.5"/><path d="M2 10l10-5 10 5-10 5z"/>`,
  "headphones": `<path d="M3 18v-6a9 9 0 0 1 18 0v6"/><path d="M21 19a2 2 0 0 1-2 2h-1v-7h3z"/><path d="M3 19a2 2 0 0 0 2 2h1v-7H3z"/>`,
  "users-round": `<circle cx="9" cy="9" r="4"/><path d="M2 21a7 7 0 0 1 14 0"/><circle cx="17" cy="6" r="3.5"/><path d="M22 17a5 5 0 0 0-9-3"/>`,
  "milestone": `<path d="M12 13v8"/><path d="M12 3v3"/><path d="M4 6h13l3 3.5L17 13H4z"/>`,
  "rocket": `<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09Z"/><path d="M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2Z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>`,
  "file-text": `<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" x2="8" y1="13" y2="13"/><line x1="16" x2="8" y1="17" y2="17"/>`,
  "message-square": `<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>`,
  "database": `<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14a9 3 0 0 0 18 0V5"/><path d="M3 12a9 3 0 0 0 18 0"/>`,
  "mic": `<rect x="9" y="2" width="6" height="12" rx="3"/><path d="M5 10a7 7 0 0 0 14 0"/><line x1="12" x2="12" y1="19" y2="22"/>`,
  "users": `<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>`,
  "playback": `<polygon points="5 3 19 12 5 21 5 3"/>`,
  "send": `<path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>`,
  "sparkles": `<path d="M9.94 5.74A2 2 0 0 0 12 4a2 2 0 0 0 2.06 1.74l1.18.39a2 2 0 0 1 0 3.74l-1.18.39A2 2 0 0 0 12 12a2 2 0 0 0-2.06-1.74l-1.18-.39a2 2 0 0 1 0-3.74Z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/>`,
  "users-2": `<path d="M14 19a6 6 0 0 0-12 0"/><circle cx="8" cy="9" r="4"/><path d="M22 19a6 6 0 0 0-6-6 4 4 0 1 0 0-8"/>`,
  "zap": `<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>`,
  "monitor": `<rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/>`,
  "minus": `<path d="M5 12h14"/>`,
  "ban": `<circle cx="12" cy="12" r="10"/><line x1="4.93" x2="19.07" y1="4.93" y2="19.07"/>`,
  "instagram": `<rect x="2" y="2" width="20" height="20" rx="5"/><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/><line x1="17.5" x2="17.51" y1="6.5" y2="6.5"/>`,
};
// aliases used in i18n
ICONS["pipeline"]  = ICONS["zap"];
ICONS["script"]    = ICONS["file-text"];
ICONS["objection"] = ICONS["message-square"];
ICONS["crm"]       = ICONS["database"];
ICONS["recording"] = ICONS["mic"];
ICONS["team"]      = ICONS["users"];
ICONS["review"]    = ICONS["playback"];
ICONS["slack"]     = ICONS["sparkles"];

function Icon({ name, size = 18, strokeWidth = 1.6, style = {} }) {
  const body = ICONS[name] || "";
  return (
    <svg
      width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth={strokeWidth}
      strokeLinecap="round" strokeLinejoin="round"
      style={style}
      dangerouslySetInnerHTML={{ __html: body }}
    />
  );
}

Object.assign(window, {
  LangProvider, useLang, useReducedMotion,
  Reveal, DividerLine, Magnetic, useTilt, CountUp, Icon,
});
