/* global React */
// landing-hero.jsx — Top nav + hero section + animated bond canvas + inline waitlist

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

// ─── Shared waitlist API + Turnstile hook (used by hero and body forms) ───
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

async function submitWaitlistAPI(payload) {
  const endpoint = window.VALENCE_WAITLIST_ENDPOINT || "/api/waitlist";
  const res = await fetch(endpoint, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(payload),
  });
  let data = {};
  try { data = await res.json(); } catch {}
  if (!res.ok) throw new Error(data.error || "request_failed");
  return data;
}

function useTurnstile() {
  const mountRef = useRef(null);
  const tokenRef = useRef("");
  const [ready, setReady] = useState(false);
  useEffect(() => {
    const sitekey = window.VALENCE_TURNSTILE_SITEKEY;
    if (!sitekey || sitekey.startsWith("REPLACE_")) {
      // Not configured — let the form submit anyway (server will block unless secret also unset).
      setReady(true);
      return;
    }
    let widgetId, cancelled = false;
    const tryRender = () => {
      if (cancelled) return;
      const el = mountRef.current;
      if (window.turnstile && el && !el.dataset.tsRendered) {
        widgetId = window.turnstile.render(el, {
          sitekey,
          theme: "dark",
          size: "flexible",
          callback: (t) => { tokenRef.current = t; setReady(true); },
          "expired-callback": () => { tokenRef.current = ""; setReady(false); },
          "error-callback":   () => { tokenRef.current = ""; setReady(false); },
        });
        el.dataset.tsRendered = "1";
      } else {
        setTimeout(tryRender, 200);
      }
    };
    tryRender();
    return () => {
      cancelled = true;
      if (widgetId && window.turnstile) {
        try { window.turnstile.remove(widgetId); } catch {}
      }
    };
  }, []);
  return { mountRef, getToken: () => tokenRef.current, ready };
}

window.submitWaitlistAPI = submitWaitlistAPI;
window.useTurnstile = useTurnstile;
window.EMAIL_RE = EMAIL_RE;

// ─── Logo lockup (inline SVG so colors invert with theme) ─────────────────
function LockupInline({ light = true }) {
  const bond = light ? "#FFFFFF" : "#2C3E50";
  const word = light ? "#FFFFFF" : "#2C3E50";
  return (
    <svg viewBox="0 0 320 80" width="120" height="30" role="img" aria-label="Valence">
      <g transform="translate(46 40)">
        <g fill="none" stroke={bond} strokeWidth="3" strokeLinecap="round">
          <line x1="0" y1="-22" x2="0" y2="22" />
          <line x1="0" y1="-22" x2="-22" y2="11" />
          <line x1="0" y1="22" x2="-22" y2="11" />
        </g>
        <circle cx="0" cy="-22" r="9" fill="#B87333" />
        <circle cx="0" cy="-22" r="4" fill="#FFFFFF" />
        <circle cx="-22" cy="11" r="9" fill="#B87333" />
        <circle cx="-22" cy="11" r="4" fill="#FFFFFF" />
        <circle cx="0" cy="22" r="9" fill="#B87333" />
        <circle cx="0" cy="22" r="4" fill="#FFFFFF" />
      </g>
      <text x="78" y="50" fontFamily="Plus Jakarta Sans, sans-serif" fontWeight="700" fontSize="26" letterSpacing="3" fill={word}>VALENCE</text>
    </svg>
  );
}

// ─── Top floating nav ─────────────────────────────────────────────────────
function TopNav({ inLight, onJoin }) {
  return (
    <nav className="nav" data-light={inLight ? "true" : "false"}>
      <a href="#top" className="nav-lockup">
        <LockupInline light={!inLight} />
      </a>
      <div className="nav-links">
        <a href="#how">How it works</a>
        <a href="#day">A day with Valence</a>
        <a href="#faq">FAQ</a>
      </div>
      <button className="nav-cta" onClick={onJoin}>Join the waitlist</button>
    </nav>
  );
}

// ─── Stagger-revealed headline ────────────────────────────────────────────
function RevealedHeadline({ text, accentWord, keyHint }) {
  // Split into words to preserve no-wrap; letters animate per word.
  const words = text.split(" ");
  let charIndex = 0;
  return (
    <h1 key={keyHint} className="hero-headline">
      {words.map((w, wi) => {
        const isAccent = accentWord && w.replace(/[.,]/g, "").toLowerCase() === accentWord.toLowerCase();
        const span = (
          <span key={wi} className="word" style={{ display: "inline-block", whiteSpace: "nowrap" }}>
            {[...w].map((ch, ci) => {
              const delay = (charIndex++) * 22;
              return (
                <span
                  key={ci}
                  className="letter"
                  style={{
                    animationDelay: `${delay}ms`,
                    background: isAccent ? "var(--va-gradient-accent)" : "none",
                    WebkitBackgroundClip: isAccent ? "text" : "initial",
                    backgroundClip: isAccent ? "text" : "initial",
                    color: isAccent ? "transparent" : "inherit",
                  }}
                >{ch}</span>
              );
            })}
          </span>
        );
        return (
          <React.Fragment key={`f${wi}`}>
            {span}
            {wi < words.length - 1 ? " " : ""}
          </React.Fragment>
        );
      })}
    </h1>
  );
}

// ─── Constellation bond canvas ────────────────────────────────────────────
// Drifting copper + slate nodes that link as bonds when they pass within
// range. The cursor pulls copper bonds toward itself.
function BondCanvas({ intensity, palette }) {
  const canvasRef = useRef(null);
  const rafRef = useRef(0);
  const stateRef = useRef(null);
  const mouseRef = useRef({ x: 0, y: 0, active: false });

  // palette may be a hex string (from TweakColor) or a name; both work
  const named = { copper: "#B87333", bronze: "#9C7A4E", rust: "#C2522A" };
  const copper = named[palette] || (typeof palette === "string" && palette.startsWith("#") ? palette : "#B87333");

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let w = 0, h = 0, dpr = Math.min(window.devicePixelRatio || 1, 2);

    function resize() {
      const rect = canvas.getBoundingClientRect();
      w = rect.width; h = rect.height;
      canvas.width = Math.floor(w * dpr);
      canvas.height = Math.floor(h * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }
    resize();
    window.addEventListener("resize", resize);

    function init() {
      const count = Math.max(40, Math.floor((w * h) / 18000));
      const nodes = [];
      for (let i = 0; i < count; i++) {
        nodes.push({
          x: Math.random() * w, y: Math.random() * h,
          vx: (Math.random() - 0.5) * 0.25,
          vy: (Math.random() - 0.5) * 0.25,
          r: 1.5 + Math.random() * 2.2,
          isCopper: Math.random() < 0.18,
        });
      }
      stateRef.current = { nodes };
    }
    init();

    function frame() {
      const speed = 0.4 + (intensity / 100) * 1.2;
      ctx.clearRect(0, 0, w, h);

      const { nodes } = stateRef.current;
      for (const n of nodes) {
        n.x += n.vx * speed; n.y += n.vy * speed;
        if (n.x < 0 || n.x > w) n.vx *= -1;
        if (n.y < 0 || n.y > h) n.vy *= -1;
      }

      const mouse = mouseRef.current;
      const linkDist = 110;
      ctx.lineCap = "round";
      for (let i = 0; i < nodes.length; i++) {
        for (let j = i + 1; j < nodes.length; j++) {
          const a = nodes[i], b = nodes[j];
          const dx = a.x - b.x, dy = a.y - b.y;
          const d2 = dx * dx + dy * dy;
          if (d2 < linkDist * linkDist) {
            const alpha = (1 - Math.sqrt(d2) / linkDist) * 0.35;
            ctx.strokeStyle = `rgba(229,234,240,${alpha.toFixed(3)})`;
            ctx.lineWidth = 1;
            ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();
          }
        }
      }

      for (const n of nodes) {
        if (n.isCopper) {
          ctx.fillStyle = copper;
          ctx.beginPath(); ctx.arc(n.x, n.y, n.r + 1.4, 0, Math.PI * 2); ctx.fill();
          ctx.fillStyle = "#FFFFFF";
          ctx.beginPath(); ctx.arc(n.x, n.y, (n.r + 1.4) * 0.35, 0, Math.PI * 2); ctx.fill();
        } else {
          ctx.fillStyle = "rgba(229,234,240,0.7)";
          ctx.beginPath(); ctx.arc(n.x, n.y, n.r * 0.7, 0, Math.PI * 2); ctx.fill();
        }
      }

      if (mouse.active) {
        for (const n of nodes) {
          const dx = n.x - mouse.x, dy = n.y - mouse.y;
          const d2 = dx * dx + dy * dy;
          if (d2 < 140 * 140) {
            const alpha = (1 - Math.sqrt(d2) / 140) * 0.6;
            ctx.strokeStyle = `rgba(184,115,51,${alpha.toFixed(3)})`;
            ctx.lineWidth = 1.2;
            ctx.beginPath(); ctx.moveTo(mouse.x, mouse.y); ctx.lineTo(n.x, n.y); ctx.stroke();
          }
        }
      }

      rafRef.current = requestAnimationFrame(frame);
    }
    rafRef.current = requestAnimationFrame(frame);

    function onMove(e) {
      const rect = canvas.getBoundingClientRect();
      mouseRef.current.x = e.clientX - rect.left;
      mouseRef.current.y = e.clientY - rect.top;
      mouseRef.current.active = true;
    }
    function onLeave() { mouseRef.current.active = false; }
    canvas.addEventListener("mousemove", onMove);
    canvas.addEventListener("mouseleave", onLeave);

    return () => {
      cancelAnimationFrame(rafRef.current);
      window.removeEventListener("resize", resize);
      canvas.removeEventListener("mousemove", onMove);
      canvas.removeEventListener("mouseleave", onLeave);
    };
  }, [intensity, palette]);

  return <canvas ref={canvasRef} style={{ position: "absolute", inset: 0, width: "100%", height: "100%", display: "block" }} />;
}

// ─── Inline waitlist form (hero) — 3 variants ─────────────────────────────
function InlineWaitlist({ variant }) {
  const [email, setEmail] = useState("");
  const [name, setName] = useState("");
  const [role, setRole] = useState("Broker");
  const [firm, setFirm] = useState("");
  const [hp, setHp] = useState(""); // honeypot
  const [submitted, setSubmitted] = useState(false);
  const [position, setPosition] = useState(null);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState("");
  const { mountRef: tsMountRef, getToken: getTsToken } = useTurnstile();

  const submit = async (e) => {
    e.preventDefault();
    if (submitting) return;
    setError("");
    if (!EMAIL_RE.test(email)) { setError("Please enter a valid email."); return; }
    setSubmitting(true);
    try {
      const out = await submitWaitlistAPI({
        email, name, role, firm, hp, tsToken: getTsToken(),
      });
      setPosition(out.position);
      setSubmitted(true);
    } catch (err) {
      const code = String(err.message || "");
      setError(
        code === "turnstile_failed" ? "Please complete the verification." :
        code === "bad_email"        ? "Please enter a valid email." :
                                      "Couldn't reach the server. Try again in a moment."
      );
    } finally {
      setSubmitting(false);
    }
  };

  if (submitted) {
    return (
      <div className="hero-waitlist-success">
        <div className="success-row">
          <span className="check">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
              <path d="M5 12l5 5 9-11" stroke="#3D9970" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </span>
          <div>
            <div className="success-title">You're in.</div>
            <div className="success-sub">Position <span className="num">#{position}</span> · skip ahead — share with a colleague.</div>
          </div>
        </div>
        <div className="share-row">
          <button className="share-btn" onClick={() => navigator.clipboard && navigator.clipboard.writeText(window.location.href)}>Copy referral link</button>
          <a className="share-link" href={`mailto:?subject=Try Valence with me&body=I'm joining the Valence waitlist. Want in? ${window.location.href}`}>Email a colleague</a>
        </div>
      </div>
    );
  }

  // variants: 0 = email only • 1 = email + role • 2 = email + name + firm
  return (
    <form onSubmit={submit} className={`hero-waitlist v${variant}`} noValidate>
      {variant === 2 && (
        <div className="row">
          <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Your name" aria-label="Name" />
          <input value={firm} onChange={(e) => setFirm(e.target.value)} placeholder="Brokerage / firm" aria-label="Firm" />
        </div>
      )}
      <div className="row primary">
        <input type="email" required value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@firm.com" aria-label="Email" autoComplete="email" />
        {variant === 1 && (
          <select value={role} onChange={(e) => setRole(e.target.value)} aria-label="Role">
            <option>Broker</option>
            <option>Sales rep</option>
            <option>Consultant</option>
            <option>Agency lead</option>
            <option>Founder</option>
            <option>Other</option>
          </select>
        )}
        <button type="submit" className="cta" disabled={submitting}>
          <span>{submitting ? "Sending…" : "Request access"}</span>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none"><path d="M5 12h14M13 5l7 7-7 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /></svg>
        </button>
      </div>
      {/* Honeypot — hidden from humans, often filled by bots. */}
      <input
        type="text" tabIndex={-1} autoComplete="off" aria-hidden="true"
        name="company_url" value={hp} onChange={(e) => setHp(e.target.value)}
        style={{ position: "absolute", left: "-9999px", width: 1, height: 1, opacity: 0 }}
      />
      <div ref={tsMountRef} className="turnstile-mount" />
      {error && <p className="form-error" role="alert">{error}</p>}
      <p className="fineprint">No spam. We open seats in waves — early signups go first.</p>
    </form>
  );
}

// ─── Hero composition ─────────────────────────────────────────────────────
const HERO_HEADLINES = [
  { text: "Stop chasing rows. Start closing deals.", accent: "deals.", eyebrow: "For brokers & sales reps in private beta" },
  { text: "Your book of business — finally bonded.", accent: "bonded.", eyebrow: "The CRM that lives in your head, not your inbox" },
  { text: "Every contact, every callback — bonded together.", accent: "bonded", eyebrow: "Built for the people who carry the pipeline" },
];

function Hero({ tweaks, scrollToWaitlist }) {
  const headlineIndex = useMemo(() => Math.floor(Math.random() * HERO_HEADLINES.length), []);
  const headline = HERO_HEADLINES[headlineIndex];

  // Magnetic button effect
  const btnRef = useRef(null);
  const onBtnMove = (e) => {
    const b = btnRef.current; if (!b) return;
    const rect = b.getBoundingClientRect();
    const x = e.clientX - rect.left - rect.width / 2;
    const y = e.clientY - rect.top - rect.height / 2;
    b.style.transform = `translate(${x * 0.12}px, ${y * 0.15}px)`;
  };
  const onBtnLeave = () => { if (btnRef.current) btnRef.current.style.transform = ""; };

  return (
    <section id="top" className="hero va-on-dark">
      <div className="hero-bg">
        <div className="hero-aurora" aria-hidden="true" />
        <BondCanvas intensity={tweaks.motionIntensity} palette={tweaks.palette} />
        <div className="hero-vignette" />
      </div>

      <div className="wrap hero-content">
        <div className="hero-eyebrow">
          <span className="dot" />
          <span>{headline.eyebrow}</span>
        </div>

        <RevealedHeadline key={headlineIndex} text={headline.text} accentWord={headline.accent} keyHint={headlineIndex} />

        <p className="hero-sub">
          The names. The callbacks. The deal stages you keep in your head. <br />
          Valence puts your entire book of business in one calm place — and quietly tells you who to call next.
        </p>

        <InlineWaitlist variant={tweaks.waitlistVariant} />

        <div className="hero-meta">
          <span className="num">487</span> brokers on the list · opening seats this quarter
        </div>
      </div>

      <style>{`
        .hero {
          position: relative; min-height: 100vh; padding: 140px 0 80px;
          background: radial-gradient(1200px 700px at 50% 0%, #1a2230 0%, #0F1620 55%, #0F1620 100%);
          overflow: hidden;
        }
        .hero-bg { position: absolute; inset: 0; overflow: hidden; }

        /* ─── Aurora ─────────────────────────────────────────────────────
           Aceternity-style: a repeating-linear-gradient stripe in copper
           tones is masked by a second repeating-linear-gradient of the
           hero base color so the aurora reads as wispy ribbons rather
           than a solid wash. The base layer holds the composition; the
           ::after replays the same composition at a different size with
           mix-blend-mode: difference and animates background-position,
           which creates the flickering, drifting movement of light. */
        .hero-aurora {
          --aurora: repeating-linear-gradient(
            100deg,
            #B87333 10%,
            #D9A079 15%,
            #E5BB94 20%,
            #C2522A 25%,
            #8B5A2B 30%
          );
          --dark-gradient: repeating-linear-gradient(
            100deg,
            #0F1620 0%,
            #0F1620 7%,
            transparent 10%,
            transparent 12%,
            #0F1620 16%
          );
          position: absolute; inset: -10px; pointer-events: none;
          background-image: var(--dark-gradient), var(--aurora);
          background-size: 300%, 200%;
          background-position: 50% 50%, 50% 50%;
          filter: blur(10px);
          opacity: 0.5;
          will-change: transform;
          -webkit-mask-image: linear-gradient(to bottom, black 0%, black 55%, transparent 95%);
                  mask-image: linear-gradient(to bottom, black 0%, black 55%, transparent 95%);
        }
        .hero-aurora::after {
          content: "";
          position: absolute; inset: 0;
          background-image: var(--dark-gradient), var(--aurora);
          background-size: 200%, 100%;
          background-attachment: fixed;
          mix-blend-mode: difference;
          animation: aurora 60s linear infinite;
        }
        @keyframes aurora {
          from { background-position: 50% 50%, 50% 50%; }
          to   { background-position: 350% 50%, 350% 50%; }
        }
        @media (prefers-reduced-motion: reduce) {
          .hero-aurora::after { animation: none; }
        }

        /* Subtle edge falloff to keep content focused — no bottom fade. */
        .hero-vignette {
          position: absolute; inset: 0; pointer-events: none;
          background: radial-gradient(80% 60% at 50% 50%, transparent 55%, rgba(10,16,24,0.45) 100%);
        }
        .hero-content { position: relative; z-index: 2; text-align: center; padding-top: 24px; }

        .hero-eyebrow {
          display: inline-flex; align-items: center; gap: 8px;
          padding: 6px 14px; border-radius: 999px;
          background: rgba(255,255,255,0.05);
          border: 1px solid rgba(255,255,255,0.10);
          font-size: 12px; color: var(--va-text-secondary);
          font-weight: 500; letter-spacing: 0.02em;
          backdrop-filter: blur(8px);
        }
        .hero-eyebrow .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--va-copper); box-shadow: 0 0 12px var(--va-copper); animation: pulse-dot 2s ease-in-out infinite; }
        @keyframes pulse-dot { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; } }

        .hero-headline {
          font-family: var(--va-font-display); font-weight: 700;
          font-size: clamp(40px, 6.4vw, 84px); line-height: 1.02; letter-spacing: -0.025em;
          color: #FFFFFF; margin: 28px auto 24px; max-width: 16ch; text-wrap: balance;
        }

        .hero-sub {
          font-size: clamp(16px, 1.4vw, 19px); line-height: 1.55;
          color: rgba(229,234,240,0.75); max-width: 620px; margin: 0 auto 36px;
          font-weight: 400;
        }

        .hero-waitlist { max-width: 560px; margin: 0 auto; display: flex; flex-direction: column; gap: 10px; }
        .hero-waitlist .row { display: flex; gap: 8px; }
        .hero-waitlist.v2 .row:first-child input { flex: 1; }
        .hero-waitlist .row.primary { background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12); border-radius: 999px; padding: 6px; box-shadow: 0 8px 32px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.04); transition: border-color 0.2s; }
        .hero-waitlist .row.primary:focus-within { border-color: var(--va-copper); box-shadow: 0 8px 32px rgba(184,115,51,0.25), 0 0 0 4px rgba(184,115,51,0.10); }
        .hero-waitlist input, .hero-waitlist select {
          background: transparent; border: 0; color: #fff;
          font-family: inherit; font-size: 15px; flex: 1; padding: 10px 14px; min-width: 0;
        }
        .hero-waitlist input::placeholder { color: rgba(229,234,240,0.45); }
        .hero-waitlist input:focus, .hero-waitlist select:focus { outline: none; }
        .hero-waitlist select { color: rgba(229,234,240,0.85); appearance: none; padding-right: 24px; cursor: pointer; }

        .hero-waitlist.v2 .row:first-child { background: transparent; padding: 0; gap: 8px; }
        .hero-waitlist.v2 .row:first-child input {
          background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.10); border-radius: 999px;
          padding: 12px 18px; font-size: 14px;
        }

        .hero-waitlist .cta {
          background: var(--va-gradient-accent); color: #fff; border: 0; border-radius: 999px;
          padding: 10px 22px; font-size: 14px; font-weight: 600; cursor: pointer;
          display: inline-flex; align-items: center; gap: 8px; white-space: nowrap;
          box-shadow: 0 6px 18px rgba(184,115,51,0.45);
          transition: transform 0.15s var(--va-ease);
        }
        .hero-waitlist .cta:hover:not(:disabled) { transform: translateY(-1px); }
        .hero-waitlist .cta:disabled { opacity: 0.6; cursor: not-allowed; }
        .hero-waitlist .turnstile-mount { display: flex; justify-content: center; min-height: 0; }
        .hero-waitlist .turnstile-mount:not(:empty) { min-height: 65px; }
        .hero-waitlist .form-error { margin: 0; font-size: 13px; color: #FF8A7A; text-align: center; }
        .hero-waitlist .fineprint { margin: 4px 0 0; font-size: 12px; color: rgba(229,234,240,0.5); text-align: center; }

        .hero-waitlist-success { max-width: 560px; margin: 0 auto; padding: 24px; background: rgba(61,153,112,0.10); border: 1px solid rgba(61,153,112,0.35); border-radius: 22px; }
        .success-row { display: flex; gap: 14px; align-items: flex-start; }
        .check { width: 36px; height: 36px; border-radius: 50%; background: rgba(61,153,112,0.20); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
        .success-title { font-family: var(--va-font-display); font-weight: 700; font-size: 22px; color: #fff; }
        .success-sub { color: rgba(229,234,240,0.72); font-size: 14px; margin-top: 2px; }
        .share-row { display: flex; gap: 8px; margin-top: 18px; }
        .share-btn { background: rgba(255,255,255,0.08); color: #fff; border: 1px solid rgba(255,255,255,0.12); padding: 8px 16px; border-radius: 999px; font-size: 13px; font-weight: 500; cursor: pointer; }
        .share-btn:hover { background: rgba(255,255,255,0.14); }
        .share-link { color: var(--va-copper); font-size: 13px; padding: 8px 12px; align-self: center; }

        .hero-meta { margin-top: 22px; font-size: 13px; color: rgba(229,234,240,0.5); }
        .hero-meta .num { color: var(--va-copper); font-weight: 600; font-variant-numeric: tabular-nums; }
      `}</style>
    </section>
  );
}

window.TopNav = TopNav;
window.Hero = Hero;
window.LockupInline = LockupInline;
