// Apply.jsx — verified-email job application flow.
//
// Route:  #/apply            (general application)
//         #/apply?job=<slug> (preselects an open position; slug passed down
//                             as the `job` prop by App.jsx)
//
// Three steps against the api (see api/server.js "careers apply flow"):
//   1. email + Turnstile  → POST /api/apply/request-code (6-digit code emailed)
//   2. code               → POST /api/apply/verify-code  (sets apply_jwt cookie)
//   3. application + PDF  → POST /api/apply/submit       (relayed to the team)
//
// Nothing is stored server-side; the resume is relayed as an email attachment.

const { useState: useSAp, useEffect: useEAp, useRef: useRAp } = React;

const MAX_RESUME_BYTES = 5 * 1024 * 1024;

function ApplyPage({ onNavigate, job, thanks }) {
  // step: "email" | "code" | "form" — success navigates to #/apply/thanks
  const [step, setStep] = useSAp("email");
  const [submitted, setSubmitted] = useSAp(null); // { email, jobTitle } after a successful submit
  const [busy, setBusy] = useSAp(false);
  const [errorMsg, setErrorMsg] = useSAp("");
  const [email, setEmail] = useSAp("");
  const [code, setCode] = useSAp("");
  const [hpCheck, setHpCheck] = useSAp(false); // honeypot — must stay false
  const [turnstileToken, setTurnstileToken] = useSAp("");
  const [resendAt, setResendAt] = useSAp(0);
  const [tick, setTick] = useSAp(Date.now());
  const [jobs, setJobs] = useSAp([]);
  const [form, setForm] = useSAp({ name: "", linkedin: "", message: "", job: "" });
  const [file, setFile] = useSAp(null);
  const turnstileMountRef = useRAp(null);
  const turnstileWidgetIdRef = useRAp(null);

  // ── Turnstile: the effect owns the widget's whole lifecycle. Steps 1 and 2
  // each need a token (initial send / resend are separate, single-use tokens),
  // so on every step change the old widget is removed and a fresh one mounts.
  useEAp(() => {
    // Also keyed on `thanks`: after a submit the step resets to "email" while
    // the thanks view is shown (no mount node) — returning to #/apply must
    // re-run this effect to mount a fresh widget.
    if (thanks || (step !== "email" && step !== "code")) return;
    let cancelled = false;
    const mount = () => {
      if (cancelled || !turnstileMountRef.current || !window.turnstile) return;
      if (turnstileWidgetIdRef.current !== null) return;
      turnstileWidgetIdRef.current = window.turnstile.render(turnstileMountRef.current, {
        sitekey: window.TURNSTILE_SITE_KEY || "",
        theme: "dark",
        callback: (token) => setTurnstileToken(token),
        "error-callback": () => setTurnstileToken(""),
        "expired-callback": () => setTurnstileToken(""),
      });
    };
    let interval = null;
    if (window.turnstile) {
      mount();
    } else {
      interval = setInterval(() => {
        if (window.turnstile) { clearInterval(interval); mount(); }
      }, 150);
    }
    return () => {
      cancelled = true;
      if (interval) clearInterval(interval);
      if (turnstileWidgetIdRef.current !== null && window.turnstile) {
        try { window.turnstile.remove(turnstileWidgetIdRef.current); } catch (e) { /* widget already gone */ }
      }
      turnstileWidgetIdRef.current = null;
      setTurnstileToken("");
    };
  }, [step, thanks]);

  // Open positions for the dropdown (archived = closed, hidden).
  useEAp(() => {
    let dead = false;
    if (!window.LuliusContent) return;
    window.LuliusContent.byKind("career")
      .then((es) => { if (!dead) setJobs((es || []).filter((e) => !e.archived)); })
      .catch(() => {});
    return () => { dead = true; };
  }, []);

  // Preselect from #/apply?job=<slug> — keyed on the prop because React reuses
  // this component instance across /apply?job=… hash changes.
  useEAp(() => {
    if (job && jobs.some((j) => j.slug === job)) {
      setForm((f) => ({ ...f, job }));
    }
  }, [job, jobs]);

  // 1s tick drives the resend countdown.
  useEAp(() => {
    if (step !== "code") return;
    const i = setInterval(() => setTick(Date.now()), 1000);
    return () => clearInterval(i);
  }, [step]);
  const resendWait = Math.max(0, Math.ceil((resendAt - tick) / 1000));

  const fail = (msg) => { setErrorMsg(msg); setBusy(false); };

  const resetWidgetForFreshToken = () => {
    if (window.turnstile && turnstileWidgetIdRef.current !== null) {
      try { window.turnstile.reset(turnstileWidgetIdRef.current); } catch (e) { /* ignore */ }
    }
    setTurnstileToken("");
  };

  const requestCode = async () => {
    if (busy || !turnstileToken) return;
    setBusy(true);
    setErrorMsg("");
    try {
      const r = await fetch("/api/apply/request-code", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, turnstileToken, _hp_check: hpCheck }),
      });
      const j = await r.json().catch(() => ({ ok: false, error: "Bad server response." }));
      if (r.ok && j.ok) {
        setResendAt(Date.now() + 60 * 1000);
        setTick(Date.now());
        setCode("");
        if (step === "email") {
          setStep("code"); // step change remounts a fresh widget for the resend path
        } else {
          resetWidgetForFreshToken(); // resend on step 2: token consumed, mint another
        }
        setBusy(false);
      } else {
        resetWidgetForFreshToken();
        fail(j.error || "Something went wrong. Try again in a moment.");
      }
    } catch (err) {
      fail("Network error. Check your connection and try again.");
    }
  };

  const verifyCode = async () => {
    if (busy || code.length !== 6) return;
    setBusy(true);
    setErrorMsg("");
    try {
      const r = await fetch("/api/apply/verify-code", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, code }),
      });
      const j = await r.json().catch(() => ({ ok: false, error: "Bad server response." }));
      if (r.ok && j.ok) {
        setStep("form");
        setBusy(false);
        setErrorMsg("");
      } else {
        fail(j.error || "Verification failed.");
      }
    } catch (err) {
      fail("Network error. Check your connection and try again.");
    }
  };

  const submitApp = async (e) => {
    e.preventDefault();
    if (busy) return;
    if (!form.name.trim()) return fail("Name is required.");
    if (!file) return fail("Attach your resume (PDF).");
    if (file.size > MAX_RESUME_BYTES) return fail("Resume exceeds 5 MB.");
    setBusy(true);
    setErrorMsg("");
    try {
      const fd = new FormData();
      fd.append("name", form.name);
      fd.append("linkedin", form.linkedin);
      fd.append("message", form.message);
      fd.append("job", form.job);
      fd.append("resume", file, file.name);
      // No Content-Type header — the browser sets the multipart boundary.
      // The apply_jwt cookie rides along automatically (same origin).
      const r = await fetch("/api/apply/submit", { method: "POST", body: fd });
      const j = await r.json().catch(() => ({ ok: false, error: "Bad server response." }));
      if (r.ok && j.ok) {
        setSubmitted({ email, jobTitle: form.job ? jobTitle(form.job) : "" });
        // Reset the flow so a return visit starts fresh (session cookie is
        // cleared server-side; a new application needs a new code anyway).
        setStep("email");
        setForm({ name: "", linkedin: "", message: "", job: "" });
        setFile(null);
        setCode("");
        setBusy(false);
        onNavigate("/apply/thanks");
      } else if (r.status === 401) {
        setStep("email");
        fail("Session expired — verify your email again.");
      } else {
        fail(j.error || "Something went wrong. Try again in a moment.");
      }
    } catch (err) {
      fail("Network error. Check your connection and try again.");
    }
  };

  const inputStyle = {
    background: "transparent",
    border: "none",
    borderBottom: "1px solid var(--line-2)",
    color: "var(--paper)",
    padding: "14px 0 12px",
    fontFamily: "var(--f-body)",
    fontSize: 16,
    width: "100%",
    outline: "none",
  };
  const labelStyle = {
    fontFamily: "var(--f-mono)", fontSize: 12, letterSpacing: ".14em",
    textTransform: "uppercase", color: "var(--mute)",
    display: "block", marginBottom: 8,
  };
  const stepLabel = { email: "STEP 1 / 3 · IDENTIFY", code: "STEP 2 / 3 · VERIFY", form: "STEP 3 / 3 · APPLICATION" }[step];

  const jobTitle = (slug) => (jobs.find((j) => j.slug === slug) || {}).title;

  // ── Thank-you page (#/apply/thanks) — post-submit confirmation with its own
  // route so applications register as a distinct page_view in analytics.
  // Renders generically on a direct visit (no in-memory submission state).
  if (thanks) {
    return (
      <main>
        <section style={{ padding: "180px 0 100px", borderBottom: "1px solid var(--line)" }}>
          <div className="wrap" style={{ maxWidth: 820 }}>
            <div className="eyebrow accent-text" style={{ marginBottom: 20 }}>
              <span className="dot"></span>APPLICATION RECEIVED
            </div>
            <h1 className="display" style={{ fontSize: "clamp(44px, 7vw, 96px)", marginBottom: 28 }}>
              Thank you for<br/>raising your hand.
            </h1>
            <p className="lede mute" style={{ maxWidth: "58ch" }}>
              {submitted
                ? <>Your application{submitted.jobTitle ? <> for <span style={{ color: "var(--paper)" }}>{submitted.jobTitle}</span></> : null} is with the team.</>
                : <>Your application is with the team.</>}
              {" "}A note from our founder is on its way to
              {submitted ? <> <span style={{ color: "var(--paper)" }}>{submitted.email}</span></> : " your inbox"} —
              if there's a fit, we'll reply from there.
            </p>
            <div style={{ display: "flex", gap: 16, marginTop: 40, flexWrap: "wrap" }}>
              <a href="#/correspondence?src=apply" onClick={(e) => { e.preventDefault(); onNavigate("/correspondence?src=apply"); }} className="btn">
                Join our correspondence <span className="arrow">→</span>
              </a>
              <a href="#/careers" onClick={(e) => { e.preventDefault(); onNavigate("/careers"); }} className="btn ghost">
                Back to careers
              </a>
            </div>
          </div>
        </section>
        <ContactCTA onNavigate={onNavigate} />
      </main>
    );
  }

  return (
    <main>
      <PageHeader
        idx="07 / CAREERS · APPLY"
        title={<>State your <span className="accent-text">case.</span></>}
        lede="Verify your email, attach a resume, and it lands directly with the team — no portals, no black holes."
      />

      <section className="section">
        <div className="wrap" style={{ maxWidth: 720 }}>
          <div className="eyebrow accent-text" style={{ marginBottom: 40 }}>
            <span className="dot"></span>{stepLabel}
          </div>

          {errorMsg && (
            <div style={{
              marginBottom: 32, padding: "14px 18px",
              border: "1px solid var(--accent)", color: "var(--paper)",
              fontFamily: "var(--f-mono)", fontSize: 12, letterSpacing: ".06em",
            }}>
              ERR · {errorMsg}
            </div>
          )}

          {/* ── Step 1: email ── */}
          {step === "email" && (
            <form onSubmit={(e) => { e.preventDefault(); requestCode(); }} style={{ display: "flex", flexDirection: "column", gap: 32 }}>
              {/* Honeypot — checkbox, immune to Chrome profile autofill (see ContactPage). */}
              <div aria-hidden="true" style={{ position: "absolute", left: "-9999px", top: "auto", width: 1, height: 1, overflow: "hidden" }}>
                <input type="checkbox" name="_hp_check" tabIndex={-1} autoComplete="off"
                  checked={hpCheck} onChange={(e) => setHpCheck(e.target.checked)} />
              </div>
              <p className="lede mute" style={{ margin: 0 }}>
                We'll email you a 6-digit code to confirm you're reachable — it doubles as our spam filter.
              </p>
              <div>
                <label style={labelStyle}>Email</label>
                <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required
                  style={inputStyle} placeholder="you@example.com" autoComplete="email" />
              </div>
              <div ref={turnstileMountRef} />
              <div>
                <button type="submit" className="btn" disabled={busy || !turnstileToken}
                  style={{ opacity: busy || !turnstileToken ? 0.5 : 1 }}>
                  {busy ? "Sending…" : <>Send code <span className="arrow">→</span></>}
                </button>
              </div>
            </form>
          )}

          {/* ── Step 2: code ── */}
          {step === "code" && (
            <form onSubmit={(e) => { e.preventDefault(); verifyCode(); }} style={{ display: "flex", flexDirection: "column", gap: 32 }}>
              <p className="lede mute" style={{ margin: 0 }}>
                We sent a 6-digit code to <span style={{ color: "var(--paper)" }}>{email}</span>. It expires in 10 minutes.
              </p>
              <div>
                <label style={labelStyle}>Verification code</label>
                <input value={code} inputMode="numeric" pattern="[0-9]*" maxLength={6}
                  onChange={(e) => setCode(e.target.value.replace(/\D/g, ""))}
                  style={{ ...inputStyle, fontFamily: "var(--f-mono)", fontSize: 28, letterSpacing: ".4em" }}
                  placeholder="——————" autoComplete="one-time-code" />
              </div>
              <div ref={turnstileMountRef} />
              <div style={{ display: "flex", gap: 16, alignItems: "center", flexWrap: "wrap" }}>
                <button type="submit" className="btn" disabled={busy || code.length !== 6}
                  style={{ opacity: busy || code.length !== 6 ? 0.5 : 1 }}>
                  {busy ? "Checking…" : <>Verify <span className="arrow">→</span></>}
                </button>
                <button type="button" onClick={requestCode}
                  disabled={busy || resendWait > 0 || !turnstileToken}
                  style={{
                    background: "transparent", border: "1px solid var(--line-2)", color: "var(--paper)",
                    padding: "14px 20px", fontFamily: "var(--f-mono)", fontSize: 12, letterSpacing: ".14em",
                    textTransform: "uppercase", cursor: resendWait > 0 ? "default" : "pointer",
                    opacity: busy || resendWait > 0 || !turnstileToken ? 0.5 : 1,
                  }}>
                  {resendWait > 0 ? `Resend in ${resendWait}s` : "Resend code"}
                </button>
                <button type="button" onClick={() => { setStep("email"); setErrorMsg(""); }}
                  style={{ background: "none", border: "none", color: "var(--mute)", cursor: "pointer", fontFamily: "var(--f-mono)", fontSize: 12, letterSpacing: ".14em", textTransform: "uppercase" }}>
                  Change email
                </button>
              </div>
            </form>
          )}

          {/* ── Step 3: application ── */}
          {step === "form" && (
            <form onSubmit={submitApp} style={{ display: "flex", flexDirection: "column", gap: 32 }}>
              <p className="lede mute" style={{ margin: 0 }}>
                Verified as <span style={{ color: "var(--paper)" }}>{email}</span>.
              </p>
              <div>
                <label style={labelStyle}>Position</label>
                <select value={form.job} onChange={(e) => setForm({ ...form, job: e.target.value })}
                  style={{ ...inputStyle, appearance: "none", cursor: "pointer" }}>
                  <option value="" style={{ background: "#111" }}>General application</option>
                  {jobs.map((j) => (
                    <option key={j.slug} value={j.slug} style={{ background: "#111" }}>{j.title}</option>
                  ))}
                </select>
              </div>
              <div>
                <label style={labelStyle}>Name</label>
                <input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} required
                  style={inputStyle} placeholder="—" autoComplete="name" />
              </div>
              <div>
                <label style={labelStyle}>LinkedIn (optional)</label>
                <input value={form.linkedin} onChange={(e) => setForm({ ...form, linkedin: e.target.value })}
                  style={inputStyle} placeholder="https://linkedin.com/in/…" />
              </div>
              <div>
                <label style={labelStyle}>Brief the team (optional)</label>
                <textarea value={form.message} onChange={(e) => setForm({ ...form, message: e.target.value })}
                  rows={5} maxLength={5000} style={{ ...inputStyle, resize: "vertical", borderBottom: "1px solid var(--line-2)" }} placeholder="—" />
              </div>
              <div>
                <label style={labelStyle}>Resume · PDF · max 5 MB</label>
                <input type="file" accept="application/pdf,.pdf"
                  onChange={(e) => setFile(e.target.files && e.target.files[0] ? e.target.files[0] : null)}
                  style={{ color: "var(--mute)", fontFamily: "var(--f-mono)", fontSize: 13, padding: "12px 0" }} />
                {file && (
                  <div className="eyebrow mute" style={{ marginTop: 8 }}>
                    {file.name} · {(file.size / (1024 * 1024)).toFixed(2)} MB
                    {file.size > MAX_RESUME_BYTES && <span style={{ color: "var(--accent)" }}> · TOO LARGE</span>}
                  </div>
                )}
              </div>
              <div>
                <button type="submit" className="btn" disabled={busy}
                  style={{ opacity: busy ? 0.5 : 1, cursor: busy ? "wait" : "pointer" }}>
                  {busy ? "Submitting…" : <>Submit application <span className="arrow">→</span></>}
                </button>
              </div>
            </form>
          )}

        </div>
      </section>

      <ContactCTA onNavigate={onNavigate} />
    </main>
  );
}

window.ApplyPage = ApplyPage;
