/* Shared utilities and primitives */
const { useState, useEffect, useRef, useLayoutEffect, useMemo, useCallback, useContext } = React;

/* ─────────── input validation helpers ───────────
   Used by both forms before posting to n8n. The n8n workflow re-validates
   server-side; the client copy is for fast UX feedback and to fail closed
   when JS is enabled. Never rely on these alone for security. */

const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
function validEmail(s) {
  if (typeof s !== "string") return false;
  const t = s.trim();
  return t.length > 0 && t.length <= 254 && EMAIL_RE.test(t);
}

function validHttpsUrl(s, { maxLen = 200 } = {}) {
  if (s == null || s === "") return true;
  if (typeof s !== "string" || s.length > maxLen) return false;
  try {
    const u = new URL(s.trim());
    return u.protocol === "https:";
  } catch { return false; }
}

function clamp(s, n) {
  if (s == null) return "";
  return String(s).slice(0, n);
}

// Strip C0 + DEL control chars (built via RegExp ctor to avoid literal control
// bytes appearing in the source).
const CTRL_RE = new RegExp("[\\u0000-\\u001f\\u007f]", "g");
function stripCtrl(s) {
  if (s == null) return "";
  return String(s).replace(CTRL_RE, "");
}

function cleanText(s, n) {
  return clamp(stripCtrl(s).trim(), n);
}

/* localStorage cooldown — defense-in-depth, easily bypassed by clearing
   storage. Just stops the same browser from spamming submit. */
function checkCooldown(key, windowMs = 30000) {
  try {
    const last = Number(localStorage.getItem(key) || 0);
    return Date.now() - last >= windowMs;
  } catch { return true; }
}
function markCooldown(key) {
  try { localStorage.setItem(key, String(Date.now())); } catch {}
}

/* hCaptcha widget hook. Renders explicitly so it works after hash-route
   navigation (auto-render only fires on initial DOM scan). */
function useHCaptcha(siteKey) {
  const ref = useRef(null);
  const idRef = useRef(null);
  const [ready, setReady] = useState(false);

  useEffect(() => {
    if (!ref.current || !siteKey) return;
    let cancelled = false;
    let interval = null;
    const tryRender = () => {
      if (cancelled) return true;
      if (!window.hcaptcha || !ref.current) return false;
      try {
        idRef.current = window.hcaptcha.render(ref.current, { sitekey: siteKey });
        setReady(true);
      } catch { /* already rendered */ }
      return true;
    };
    if (!tryRender()) {
      interval = setInterval(() => { if (tryRender() && interval) clearInterval(interval); }, 150);
    }
    return () => {
      cancelled = true;
      if (interval) clearInterval(interval);
      if (idRef.current != null && window.hcaptcha) {
        try { window.hcaptcha.remove(idRef.current); } catch {}
      }
      idRef.current = null;
    };
  }, [siteKey]);

  const getToken = () => {
    if (!window.hcaptcha || idRef.current == null) return "";
    try { return window.hcaptcha.getResponse(idRef.current) || ""; } catch { return ""; }
  };
  const reset = () => {
    if (window.hcaptcha && idRef.current != null) {
      try { window.hcaptcha.reset(idRef.current); } catch {}
    }
  };

  return { ref, ready, getToken, reset };
}

/* Reveal-on-scroll helper hook */
function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            el.classList.add("in");
            io.unobserve(el);
          }
        });
      },
      { threshold: 0.15, rootMargin: "0px 0px -10% 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return ref;
}

function useCounter(target, duration = 1800, start = false) {
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!start) return;
    const t0 = performance.now();
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(target * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, duration, start]);
  return val;
}

function Reveal({ children, delay = 0, as: Tag = "div", className = "", style = {} }) {
  const ref = useReveal();
  return (
    <Tag ref={ref} className={`reveal ${className}`} style={{ transitionDelay: `${delay}ms`, ...style }}>
      {children}
    </Tag>
  );
}

function Words({ text, delay = 0, stagger = 60 }) {
  const words = String(text).split(" ");
  return (
    <>
      {words.map((w, i) => (
        <span className="word" key={i}>
          <span style={{ animationDelay: `${delay + i * stagger}ms` }}>
            {w}{i < words.length - 1 ? " " : ""}
          </span>
        </span>
      ))}
    </>
  );
}

function Cursor() {
  const ref = useRef(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.classList.add("hidden");
    const move = (e) => {
      el.classList.remove("hidden");
      el.style.left = e.clientX + "px";
      el.style.top = e.clientY + "px";
    };
    const enter = (e) => {
      const t = e.target;
      if (t.closest && t.closest("a,button,[data-magnet]")) el.classList.add("big");
      else el.classList.remove("big");
    };
    const leave = () => el.classList.add("hidden");
    window.addEventListener("mousemove", move);
    window.addEventListener("mouseover", enter);
    window.addEventListener("mouseleave", leave);
    return () => {
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseover", enter);
      window.removeEventListener("mouseleave", leave);
    };
  }, []);

  return <div ref={ref} className="cursor hidden" />;
}

function LogoMark({ dark = false }) {
  return (
    <div className="nav-logo">
      <img src="assets/logo.png" alt="AdvancedFlow" className={`brand-logo ${dark ? "on-dark" : ""}`} />
    </div>
  );
}

function ArrowIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="arrow">
      <path d="M5 12h14M13 5l7 7-7 7" />
    </svg>
  );
}

function PlusIcon() {
  return (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
      <path d="M12 5v14M5 12h14" />
    </svg>
  );
}

/* Language toggle — pill switch EN/ES */
function LangToggle({ darkMode = false }) {
  const { lang, setLang } = useT();
  return (
    <div className={`lang-toggle ${darkMode ? "on-dark" : ""}`}>
      <button
        className={`lang-btn ${lang === "en" ? "active" : ""}`}
        onClick={() => setLang("en")}
        data-magnet
      >EN</button>
      <button
        className={`lang-btn ${lang === "es" ? "active" : ""}`}
        onClick={() => setLang("es")}
        data-magnet
      >ES</button>
    </div>
  );
}

/* Nav */
function Nav({ route, navigate }) {
  const { t } = useT();
  const [dark, setDark] = useState(route === "home");
  useEffect(() => {
    if (route !== "home") { setDark(false); return; }
    const onScroll = () => {
      const heroH = window.innerHeight * 0.85;
      setDark(window.scrollY < heroH);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, [route]);

  const go = (anchor) => (e) => {
    e.preventDefault();
    if (route !== "home") {
      navigate("home");
      setTimeout(() => { document.querySelector(anchor)?.scrollIntoView({ behavior: "smooth", block: "start" }); }, 60);
    } else {
      document.querySelector(anchor)?.scrollIntoView({ behavior: "smooth", block: "start" });
    }
  };

  return (
    <header className={`nav ${dark ? "dark" : ""}`}>
      <div className="nav-inner">
        <a href="#" onClick={(e) => { e.preventDefault(); navigate("home"); window.scrollTo({ top: 0, behavior: "smooth" }); }}>
          <LogoMark />
        </a>
        <nav className="nav-links">
          <a href="#services" className="nav-link" onClick={go("#services")}>{t.nav.services}</a>
          <a href="#process" className="nav-link" onClick={go("#process")}>{t.nav.process}</a>
          <a href="#about" className="nav-link" onClick={go("#about")}>{t.nav.about}</a>
          <a href="#faq" className="nav-link" onClick={go("#faq")}>{t.nav.faq}</a>
          <a
            href="#hackathon"
            className="nav-link nav-link-accent"
            onClick={(e) => { e.preventDefault(); navigate("hackathon"); window.scrollTo({ top: 0 }); }}
          >{t.nav.hackathon}</a>
        </nav>
        <LangToggle darkMode={dark} />
        <a
          href="#hackathon"
          className="nav-hack-mobile"
          onClick={(e) => { e.preventDefault(); navigate("hackathon"); window.scrollTo({ top: 0 }); }}
        >{t.nav.hackathon}</a>
        <a href="#start" className="btn" onClick={(e) => { e.preventDefault(); navigate("start"); window.scrollTo({ top: 0 }); }}>
          {t.nav.cta} <ArrowIcon />
        </a>
      </div>
    </header>
  );
}

Object.assign(window, {
  useReveal, useCounter, Reveal, Words, Cursor, LogoMark, ArrowIcon, PlusIcon, Nav, LangToggle,
  validEmail, validHttpsUrl, clamp, stripCtrl, cleanText, checkCooldown, markCooldown, useHCaptcha,
});
