/* Argus auth surfaces — gate, login, signup (share-link), set-password,
   forgot, reset, and the AuthShell wrapper that hosts them.

   These render outside the workspace shell (no rail / nav / status bar)
   so the user lands on a clean, centred card. Same design tokens as
   the workspace — deep slate dark by default, gold accent, IBM Plex
   chrome, Source Serif 4 reserved for body copy only.

   Routing rules (resolved by App):
     - URL contains ?share=<tok>  → signup (claim a share-link)
     - URL contains ?reset=<tok>  → reset password
     - URL contains ?welcome=<tok>→ set-password (first-time invitee)
     - URL contains ?forgot=1     → forgot
     - Session present (Api.me)   → workspace
     - Anything else              → login (with a "no invite" gate variant)
*/

const { useState: useAuthS, useEffect: useAuthE } = React;

/* ── AuthShell ────────────────────────────────────────────────────── */

function AuthShell({ children, eyebrow, title, footer }) {
  return (
    <div className="auth-shell">
      <div className="auth-brand">
        <span className="auth-brand-eye" aria-hidden="true">
          <svg viewBox="0 0 64 64" width="42" height="42" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
            <ellipse cx="32" cy="32" rx="28" ry="18"/>
            <circle cx="32" cy="32" r="12"/>
            <circle cx="32" cy="32" r="5" fill="currentColor" stroke="none"/>
          </svg>
        </span>
        <span className="auth-brand-mark">Argus</span>
      </div>

      <div className="auth-card">
        {eyebrow && <p className="auth-eyebrow">{eyebrow}</p>}
        {title && <h1 className="auth-title">{title}</h1>}
        {children}
      </div>

      <div className="auth-footer">{footer || <FooterDefault/>}</div>
    </div>
  );
}

function FooterDefault() {
  return (
    <span className="auth-footer-text">
      Akademeia · staff-facing instrument · closed beta
    </span>
  );
}

/* ── Form primitives ──────────────────────────────────────────────── */

function Field({ label, type = "text", value, onChange, autoComplete, required, minLength, hint, name }) {
  return (
    <label className="auth-field">
      <span className="auth-field-label">{label}</span>
      <input
        type={type}
        name={name}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        autoComplete={autoComplete}
        required={required}
        minLength={minLength}
      />
      {hint && <span className="auth-field-hint">{hint}</span>}
    </label>
  );
}

function StatusLine({ tone = "neutral", text }) {
  if (!text) return null;
  return <p className={`auth-status mono is-${tone}`}>{text}</p>;
}

function SubmitButton({ label, busy, busyLabel = "Working…", disabled }) {
  return (
    <button type="submit" className="auth-submit" disabled={busy || disabled}>
      <span>{busy ? busyLabel : label}</span>
      <span className="auth-submit-arrow" aria-hidden="true">→</span>
    </button>
  );
}

/* ── Gate (no invite, no session) ─────────────────────────────────── */

function ViewGate({ onSignIn }) {
  return (
    <AuthShell
      eyebrow="Access restricted"
      title="By invitation."
    >
      <p className="auth-body">
        Argus is a staff-facing instrument for the Akademeia research network.
        Entry is granted to named invitees. If you've been sent an invitation
        link, use it to enter — otherwise sign in below.
      </p>
      <button className="auth-submit" onClick={onSignIn}>
        <span>Sign in</span>
        <span className="auth-submit-arrow" aria-hidden="true">→</span>
      </button>
    </AuthShell>
  );
}

/* ── Login ────────────────────────────────────────────────────────── */

function ViewLogin({ onSignedIn, onForgot }) {
  const [email, setEmail]   = useAuthS("");
  const [pw, setPw]         = useAuthS("");
  const [busy, setBusy]     = useAuthS(false);
  const [err, setErr]       = useAuthS("");

  async function submit(e) {
    e.preventDefault();
    setBusy(true); setErr("");
    const r = await Api.login(email.trim(), pw);
    setBusy(false);
    if (!r.ok) {
      const serverMsg = r.data && typeof r.data.message === "string" ? r.data.message : null;
      setErr(r.status === 401 ? "Email or password not recognised."
           : r.status === 429 ? (serverMsg || "Too many failed sign-in attempts. Please wait 15 minutes and try again.")
           : r.status === 0   ? "Couldn't reach the server. Try again in a moment."
           : (serverMsg || "Something went wrong. Try again."));
      return;
    }
    onSignedIn();
  }

  return (
    <AuthShell eyebrow="Sign in" title="Welcome back.">
      <p className="auth-body">Sign in with the email on your invitation.</p>
      <form onSubmit={submit} className="auth-form" noValidate>
        <Field label="Email"    type="email"    value={email} onChange={setEmail} autoComplete="email" required/>
        <Field label="Password" type="password" value={pw}    onChange={setPw}    autoComplete="current-password" required/>
        <StatusLine tone="miss" text={err}/>
        <SubmitButton busy={busy} label="Sign in"/>
        <button type="button" className="auth-link" onClick={onForgot}>Forgot your password?</button>
      </form>
    </AuthShell>
  );
}

/* ── Signup (share-link claim) ────────────────────────────────────── */

function ViewSignup({ shareToken, onClaimed }) {
  const [share, setShare]   = useAuthS(null);
  const [loading, setL]     = useAuthS(true);
  const [err, setErr]       = useAuthS("");
  const [name, setName]     = useAuthS("");
  const [email, setEmail]   = useAuthS("");
  const [pw, setPw]         = useAuthS("");
  const [busy, setBusy]     = useAuthS(false);

  useAuthE(() => {
    let cancel = false;
    (async () => {
      const r = await Api.loadShareInvite(shareToken);
      if (cancel) return;
      setL(false);
      if (!r.ok) {
        setErr(r.status === 404 ? "This share-link is invalid or has been revoked." : "Couldn't load this invitation.");
        return;
      }
      setShare(r.data || {});
      if (r.data && r.data.suggested_name)  setName(r.data.suggested_name);
      if (r.data && r.data.suggested_email) setEmail(r.data.suggested_email);
    })();
    return () => { cancel = true; };
  }, [shareToken]);

  async function submit(e) {
    e.preventDefault();
    setBusy(true); setErr("");
    const r = await Api.claimShare(shareToken, { name: name.trim(), email: email.trim(), password: pw });
    setBusy(false);
    if (!r.ok) {
      setErr(r.status === 409 ? "An account already exists for that email." : "Couldn't create the account.");
      return;
    }
    onClaimed();
  }

  if (loading) return <AuthShell eyebrow="Argus" title="Loading invitation…"/>;
  if (!share)  return <AuthShell eyebrow="Argus" title="Invitation not found."><p className="auth-body">{err}</p></AuthShell>;

  return (
    <AuthShell eyebrow="Invited to Argus" title="Create your account.">
      <p className="auth-body">
        You've been invited to the closed beta. Choose a name, email, and password —
        the password is yours, not shared with the person who sent this link.
      </p>
      <form onSubmit={submit} className="auth-form" noValidate>
        <Field label="Name"     value={name}  onChange={setName}  autoComplete="name"          required/>
        <Field label="Email"    value={email} onChange={setEmail} autoComplete="email"         type="email" required/>
        <Field label="Password" value={pw}    onChange={setPw}    autoComplete="new-password" type="password" required minLength={10}
               hint="At least 10 characters. Mix letters with numbers or symbols."/>
        <StatusLine tone="miss" text={err}/>
        <SubmitButton busy={busy} label="Create account + sign in"/>
      </form>
    </AuthShell>
  );
}

/* ── Set Password (first visit with valid invite) ─────────────────── */

function ViewSetPassword({ token, onDone }) {
  const [pw, setPw]         = useAuthS("");
  const [confirm, setC]     = useAuthS("");
  const [busy, setBusy]     = useAuthS(false);
  const [err, setErr]       = useAuthS("");

  async function submit(e) {
    e.preventDefault();
    setErr("");
    if (pw !== confirm) { setErr("Passwords don't match."); return; }
    if (pw.length < 10) { setErr("Use at least 10 characters."); return; }
    setBusy(true);
    const r = await Api.setPassword(token, pw);
    setBusy(false);
    if (!r.ok) {
      setErr(r.status === 410 ? "This link has expired. Ask for a fresh invite."
           : "Couldn't save the password.");
      return;
    }
    onDone();
  }

  return (
    <AuthShell eyebrow="Welcome" title="Set your password.">
      <p className="auth-body">Choose a password for your account. You'll use it to sign in next time — the invitation link is one-time-only.</p>
      <form onSubmit={submit} className="auth-form" noValidate>
        <Field label="Password"          type="password" value={pw}      onChange={setPw} required minLength={10} autoComplete="new-password"
               hint="At least 10 characters. Mix letters with numbers or symbols."/>
        <Field label="Confirm password"  type="password" value={confirm} onChange={setC}  required minLength={10} autoComplete="new-password"/>
        <StatusLine tone="miss" text={err}/>
        <SubmitButton busy={busy} label="Set password + continue"/>
      </form>
    </AuthShell>
  );
}

/* ── Forgot ───────────────────────────────────────────────────────── */

function ViewForgot({ onBack }) {
  const [email, setEmail]   = useAuthS("");
  const [busy, setBusy]     = useAuthS(false);
  const [sent, setSent]     = useAuthS(false);

  async function submit(e) {
    e.preventDefault();
    setBusy(true);
    await Api.forgot(email.trim());      // always optimistic — server doesn't leak existence
    setBusy(false);
    setSent(true);
  }

  if (sent) return (
    <AuthShell eyebrow="Password recovery" title="Check your inbox.">
      <p className="auth-body">
        If <span className="mono">{email}</span> is on the invitation list, a one-time reset
        link has been sent. It expires an hour after it was issued.
      </p>
      <button className="auth-link" onClick={onBack}>Back to sign-in</button>
    </AuthShell>
  );

  return (
    <AuthShell eyebrow="Password recovery" title="Reset your password.">
      <p className="auth-body">Enter the email on your invitation. We'll send a one-time link to set a new password.</p>
      <form onSubmit={submit} className="auth-form" noValidate>
        <Field label="Email" type="email" value={email} onChange={setEmail} required autoComplete="email"/>
        <SubmitButton busy={busy} label="Send reset link"/>
        <button type="button" className="auth-link" onClick={onBack}>Back to sign-in</button>
      </form>
    </AuthShell>
  );
}

/* ── Reset (via emailed link) ─────────────────────────────────────── */

function ViewReset({ token, onDone }) {
  const [pw, setPw]         = useAuthS("");
  const [confirm, setC]     = useAuthS("");
  const [busy, setBusy]     = useAuthS(false);
  const [err, setErr]       = useAuthS("");

  async function submit(e) {
    e.preventDefault();
    setErr("");
    if (pw !== confirm) { setErr("Passwords don't match."); return; }
    if (pw.length < 10) { setErr("Use at least 10 characters."); return; }
    setBusy(true);
    const r = await Api.reset(token, pw);
    setBusy(false);
    if (!r.ok) {
      const serverMsg = r.data && typeof r.data.message === "string" ? r.data.message : null;
      setErr(r.status === 410 ? "This link has expired."
           : (serverMsg || "Couldn't save the password."));
      return;
    }
    onDone();
  }

  return (
    <AuthShell eyebrow="Password recovery" title="Choose a new password.">
      <p className="auth-body">This link is one-time-only and expires an hour after it was issued.</p>
      <form onSubmit={submit} className="auth-form" noValidate>
        <Field label="New password"      type="password" value={pw}      onChange={setPw} required minLength={10} autoComplete="new-password"
               hint="At least 10 characters. Mix letters with numbers or symbols."/>
        <Field label="Confirm password"  type="password" value={confirm} onChange={setC}  required minLength={10} autoComplete="new-password"/>
        <StatusLine tone="miss" text={err}/>
        <SubmitButton busy={busy} label="Save + sign in"/>
      </form>
    </AuthShell>
  );
}

Object.assign(window, { AuthShell, ViewGate, ViewLogin, ViewSignup, ViewSetPassword, ViewForgot, ViewReset });
