/* global React, ReactDOM */
const { useState, useEffect, useMemo, useRef } = React;

/* ─────────────────────────────────────────────────────────────
   Headline variants — cinematic, two-line, fragments allowed
   ───────────────────────────────────────────────────────────── */
const HEADLINES = {
  alive:   { a: "Where stories",       b: "come alive." },
  yours:   { a: "The story",           b: "only you can tell." },
  begin:   { a: "Begin where",         b: "the story begins." },
};

/* ─────────────────────────────────────────────────────────────
   Ember particles — drifting stars on the void.
   The README's controlled exception to "ember = interactive only".
   Density tweak swaps count; positions are deterministic per seed.
   ───────────────────────────────────────────────────────────── */
function Particles({ count = 28 }) {
  const dots = useMemo(() => {
    const out = [];
    // Mulberry-ish seeded shuffle so density toggles don't reflow chaos
    let s = 1337;
    const rand = () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };
    for (let i = 0; i < count; i++) {
      const big = rand() < 0.18;
      out.push({
        left:  (rand() * 100).toFixed(2) + '%',
        top:   (rand() * 100).toFixed(2) + '%',
        size:  big ? 3 + rand() * 1.2 : 1.4 + rand() * 1.2,
        opacity: big ? 0.55 + rand() * 0.25 : 0.18 + rand() * 0.25,
        delay: -(rand() * 8).toFixed(2) + 's',
        driftDelay: -(rand() * 18).toFixed(2) + 's',
        twinkleDur: (2.4 + rand() * 3.4).toFixed(2) + 's',
        driftDur: (14 + rand() * 12).toFixed(2) + 's',
        blur: big ? 0.4 : 0,
      });
    }
    return out;
  }, [count]);

  return (
    <div aria-hidden="true" className="particles">
      {dots.map((d, i) => (
        <span
          key={i}
          className="p"
          style={{
            left: d.left, top: d.top,
            width: d.size + 'px', height: d.size + 'px',
            opacity: d.opacity,
            filter: d.blur ? `blur(${d.blur}px)` : undefined,
            animationDelay: `${d.delay}, ${d.driftDelay}`,
            animationDuration: `${d.twinkleDur}, ${d.driftDur}`,
          }}
        />
      ))}
    </div>
  );
}

/* ─────────────────────────────────────────────────────────────
   Cursor glow — desktop only, screen blend on dark
   ───────────────────────────────────────────────────────────── */
function CursorGlow() {
  const ref = useRef(null);
  useEffect(() => {
    const onMove = (e) => {
      if (!ref.current) return;
      ref.current.style.transform =
        `translate(${e.clientX - 250}px, ${e.clientY - 250}px)`;
    };
    window.addEventListener('pointermove', onMove);
    return () => window.removeEventListener('pointermove', onMove);
  }, []);
  return <div ref={ref} aria-hidden="true" className="cursor-glow" />;
}

/* ─────────────────────────────────────────────────────────────
   Top bar — wovv.ai wordmark left · "Early access · 2026" right
   ───────────────────────────────────────────────────────────── */
function TopBar() {
  return (
    <header className="topbar">
      <a className="brand" href="#" aria-label="wovv.ai">
        <span className="brand-w">wovv</span>
        <span className="brand-dot" aria-hidden="true" />
        <span className="brand-ai">ai</span>
      </a>
      <div className="kicker">
        <span className="kicker-pulse" aria-hidden="true" />
        <span>Early Access · 2026</span>
      </div>
    </header>
  );
}

/* ─────────────────────────────────────────────────────────────
   Email capture — Supabase Auth magic link via Resend SMTP.
   Worker (/api/waitlist) verifies Turnstile + calls supabase.auth.signInWithOtp.
   Supabase emails the magic link via Resend (custom SMTP, brand-polished).
   User clicks → /welcome → DB trigger creates waitlist row.
   ───────────────────────────────────────────────────────────── */
function WaitlistForm({ onJoin }) {
  const [email, setEmail] = useState('');
  const [state, setState] = useState('idle'); // idle | submitting | done | error
  const [errMsg, setErrMsg] = useState('');
  const turnstileRef = useRef(null);
  const widgetId = useRef(null);

  const siteKey = useMemo(() => {
    if (typeof document === 'undefined') return '';
    const meta = document.querySelector('meta[name="turnstile-site-key"]');
    return meta ? meta.getAttribute('content') : '';
  }, []);

  useEffect(() => {
    if (!turnstileRef.current || !siteKey || siteKey.startsWith('__')) return;
    const tryRender = () => {
      if (typeof window.turnstile === 'undefined') {
        setTimeout(tryRender, 200);
        return;
      }
      if (widgetId.current !== null) return;
      widgetId.current = window.turnstile.render(turnstileRef.current, {
        sitekey: siteKey,
        theme: 'dark',
        size: 'flexible',
        appearance: 'interaction-only',
      });
    };
    tryRender();
  }, [siteKey]);

  const submit = async (e) => {
    e.preventDefault();
    setErrMsg('');

    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      setState('error');
      setErrMsg("That doesn't look like an email.");
      return;
    }

    let turnstileToken = '';
    if (window.turnstile && widgetId.current !== null) {
      try {
        turnstileToken = window.turnstile.getResponse(widgetId.current) || '';
      } catch { turnstileToken = ''; }
    }
    if (!turnstileToken) {
      setState('error');
      setErrMsg('Please complete the verification challenge.');
      return;
    }

    setState('submitting');

    const url = new URL(window.location.href);
    const payload = {
      email,
      turnstile_token: turnstileToken,
      locale: (navigator.language || 'en').slice(0, 5),
      utm_source: url.searchParams.get('utm_source'),
      utm_medium: url.searchParams.get('utm_medium'),
      utm_campaign: url.searchParams.get('utm_campaign'),
      referrer: document.referrer || null,
      ref: url.searchParams.get('ref'), // referral code from /?ref=CODE — Worker looks up referrer
    };

    try {
      const resp = await fetch('/api/waitlist', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
      });

      if (!resp.ok) {
        if (window.turnstile && widgetId.current !== null) {
          window.turnstile.reset(widgetId.current);
        }
        setState('error');
        try {
          const j = await resp.json();
          setErrMsg(j && j.error === 'turnstile_failed'
            ? 'Verification failed. Please try again.'
            : 'Something went wrong. Try again in a moment.');
        } catch {
          setErrMsg('Something went wrong. Try again in a moment.');
        }
        return;
      }

      setState('done');
      onJoin && onJoin();
    } catch {
      if (window.turnstile && widgetId.current !== null) {
        window.turnstile.reset(widgetId.current);
      }
      setState('error');
      setErrMsg('Connection issue. Try again in a moment.');
    }
  };

  if (state === 'done') {
    return (
      <div className="confirm voidEmerge" role="status">
        <div className="confirm-ring" aria-hidden="true">
          <svg viewBox="0 0 24 24" width="22" height="22" fill="none"
               stroke="currentColor" strokeWidth="1.6"
               strokeLinecap="round" strokeLinejoin="round">
            <path d="M2 7l10 7L22 7" />
            <path d="M2 7v10a2 2 0 002 2h16a2 2 0 002-2V7" />
          </svg>
        </div>
        <div>
          <div className="confirm-title">Check your inbox.</div>
          <div className="confirm-sub">
            We sent a confirmation link to <strong>{email}</strong>.
            Click it to secure your seat.
          </div>
        </div>
      </div>
    );
  }

  return (
    <form className={`form ${state === 'error' ? 'is-error' : ''}`} onSubmit={submit} noValidate>
      <div className="field">
        <input
          type="email"
          required
          autoComplete="email"
          placeholder="your@email.com"
          value={email}
          onChange={(e) => { setEmail(e.target.value); if (state === 'error') { setState('idle'); setErrMsg(''); } }}
          aria-label="Email address"
        />
        <button
          type="submit"
          className={`btn-primary ${state === 'submitting' ? 'is-loading' : ''}`}
          disabled={state === 'submitting'}
        >
          <span className="btn-label">
            {state === 'submitting' ? 'Sending link…' : 'Reserve Your Seat'}
          </span>
          <span className="btn-arrow" aria-hidden="true">→</span>
        </button>
      </div>

      <div className="turnstile-mount" ref={turnstileRef} aria-label="Verification challenge" />

      <div className="form-meta">
        {state === 'error'
          ? <span className="form-err">{errMsg}</span>
          : <span className="form-hint">We'll email a one-click confirm link. No spam. One quiet note when access opens.</span>}
      </div>
    </form>
  );
}

/* ─────────────────────────────────────────────────────────────
   What's coming — three quiet panels, luminance-stepped
   ───────────────────────────────────────────────────────────── */
const PILLARS = [
  {
    label: 'The Editor',
    title: 'A surface that disappears.',
    body: "Distraction-free prose. Manuscript, screenplay, or longform — typeset the way it'll be read.",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"
           strokeLinecap="round" strokeLinejoin="round">
        <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
        <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
        <path d="M9 7h7M9 11h7M9 15h4" />
      </svg>
    ),
  },
  {
    label: 'PlotMind',
    title: 'A collaborator, not a co-author.',
    body: 'An AI that knows your characters, your scenes, your voice. It asks the questions. You write the answers.',
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"
           strokeLinecap="round" strokeLinejoin="round">
        <path d="M12 3a4 4 0 0 0-4 4v1a4 4 0 0 0-2 7 4 4 0 0 0 6 3 4 4 0 0 0 6-3 4 4 0 0 0-2-7V7a4 4 0 0 0-4-4z" />
        <path d="M12 8v9M9 12h6" />
      </svg>
    ),
  },
  {
    label: 'Story Forge',
    title: 'Version control for ideas.',
    body: 'Branch a thread. Merge a draft. Compare timelines. Every version of the story you ever almost wrote.',
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"
           strokeLinecap="round" strokeLinejoin="round">
        <circle cx="6" cy="5" r="2" />
        <circle cx="6" cy="19" r="2" />
        <circle cx="18" cy="12" r="2" />
        <path d="M6 7v10" />
        <path d="M6 12c6 0 6-5 10-5" />
      </svg>
    ),
  },
];

function Pillars() {
  return (
    <section className="pillars" aria-label="What you'll find inside">
      {PILLARS.map((p, i) => (
        <article className="pillar" key={p.label} style={{ animationDelay: `${260 + i * 80}ms` }}>
          <div className="pillar-icon" aria-hidden="true">{p.icon}</div>
          <div className="pillar-label">{p.label}</div>
          <h3 className="pillar-title">{p.title}</h3>
          <p className="pillar-body">{p.body}</p>
        </article>
      ))}
    </section>
  );
}

/* ─────────────────────────────────────────────────────────────
   Early-access perks — appears when toggle is on
   ───────────────────────────────────────────────────────────── */
function Perks() {
  const items = [
    'First wave of invitations when the closed beta opens',
    'Direct line to the team during early access',
    'A founder seat number, kept on your account',
  ];
  return (
    <ul className="perks voidEmerge">
      {items.map((t) => (
        <li key={t}>
          <span className="perk-dot" aria-hidden="true" />
          <span>{t}</span>
        </li>
      ))}
    </ul>
  );
}

/* ─────────────────────────────────────────────────────────────
   Social proof — quiet counter line
   ───────────────────────────────────────────────────────────── */
function Counter({ joined }) {
  const base = 1287;
  const total = base + (joined ? 1 : 0);
  return (
    <div className="counter">
      <span className="counter-num">{total.toLocaleString()}</span>
      <span className="counter-text">
        {joined ? 'writers ahead of you · welcome aboard' : 'writers already in line'}
      </span>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────────
   Footer — minimal
   ───────────────────────────────────────────────────────────── */
function Footer() {
  return (
    <footer className="foot">
      <div>© wovv.ai · all stories reserved</div>
      <nav>
        <a href="#">Privacy</a>
        <a href="#">Press</a>
        <a href="mailto:hello@wovv.ai">hello@wovv.ai</a>
      </nav>
    </footer>
  );
}

/* ─────────────────────────────────────────────────────────────
   App — production config (TweaksPanel stripped per CCO + CISO binding)
   Defaults frozen at the values CCO Cruz reviews on preview deploy.
   ───────────────────────────────────────────────────────────── */
const PROD_CONFIG = {
  headline: 'alive',
  density: 28,
  showPerks: true,
  cursorGlow: true,
  chromeBreathe: true,
};

function App() {
  const [joined, setJoined] = useState(false);
  const headline = HEADLINES[PROD_CONFIG.headline] || HEADLINES.alive;

  return (
    <div className="page">
      <Particles count={PROD_CONFIG.density} />
      {PROD_CONFIG.cursorGlow && <CursorGlow />}

      <TopBar />

      <main className="stage">
        <div className="hero voidEmerge--deep">
          <div className="eyebrow">
            <span className="eyebrow-dash" aria-hidden="true" />
            Joining waitlist for PlotWeaver — the writing app from wovv.ai
          </div>

          <h1 className="headline">
            <span className={`chrome-text ${PROD_CONFIG.chromeBreathe ? 'chrome-breathe' : ''}`}>
              {headline.a}
            </span>
            <br />
            <span className={`chrome-text ${PROD_CONFIG.chromeBreathe ? 'chrome-breathe' : ''}`}>
              {headline.b}
            </span>
          </h1>

          <p className="sub">
            An AI writing platform for screenwriters, novelists, and world-builders.
            Reserve a seat now — invitations roll out in waves through the spring.
          </p>

          <WaitlistForm onJoin={() => setJoined(true)} />
          <Counter joined={joined} />

          {PROD_CONFIG.showPerks && <Perks />}
        </div>

        <Pillars />
      </main>

      <Footer />
      {/* TweaksPanel stripped for production. Design knobs frozen at PROD_CONFIG above.
          Lived previously in design-preview deploy only (tweaks-panel.jsx not loaded). */}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
