// Login + signup screens — Phase-3 multi-tenant identity.
//
// The default screen is the email/password login. From there:
//   - "Crear cuenta" → SignupScreen (3-field form + email-verification screen)
//   - The four legacy actors (founder/tlead/code/claude) can still log in,
//     but only via email — the founder's email is admin@neuronomy.ai.
//
// After email verification (clicking the link from the email), the user
// lands at /verify-email?status=ok which the App component routes to
// VerifyEmailLanding.
//
// Pending-invite carryover:
//   When an unregistered invitee opens /accept-invite/<token> and clicks
//   "Crear cuenta" or "Tengo cuenta", we stash the invite token + email in
//   localStorage so the signup / verify-email / login flows can auto-accept
//   the invite once they have a session. Without this, the invitee finishes
//   signup but never becomes a workspace member, and lands on a black/empty
//   screen because /api/me has no workspace.

const _PENDING_INVITE_KEY = 'ws.pendingInvite';
const _pendingInvite = {
  set(token, email) {
    try { localStorage.setItem(_PENDING_INVITE_KEY, JSON.stringify({ token, email })); } catch {}
  },
  get() {
    try { return JSON.parse(localStorage.getItem(_PENDING_INVITE_KEY) || 'null'); } catch { return null; }
  },
  clear() {
    try { localStorage.removeItem(_PENDING_INVITE_KEY); } catch {}
  },
};
// Best-effort accept of a stashed invite. Called after the user gains a
// session (login, signup-then-verify, or verify-email). Silently no-ops if
// there's no pending token. If the accept fails because the token is
// expired / consumed / for a different email, we still clear the stash so
// the next boot doesn't loop forever.
async function _consumePendingInvite() {
  const p = _pendingInvite.get();
  if (!p?.token) return false;
  try {
    await window.api('POST', `/api/invites/${encodeURIComponent(p.token)}/accept`);
    _pendingInvite.clear();
    return true;
  } catch (e) {
    if (e?.status >= 400 && e?.status < 500) _pendingInvite.clear();
    return false;
  }
}

function _Card({ children }) {
  // 3D: subtle parallax tilt on pointer move; glassmorphic surface with a
  // long ambient shadow so the card feels like it's floating over the
  // gradient backdrop. Falls back to a static card on touch devices.
  const ref = React.useRef(null);
  const onMove = (e) => {
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const dx = (e.clientX - (r.left + r.width / 2)) / r.width;
    const dy = (e.clientY - (r.top + r.height / 2)) / r.height;
    const ry = (dx * 6).toFixed(2);
    const rx = (-dy * 6).toFixed(2);
    el.style.transform = `perspective(900px) rotateX(${rx}deg) rotateY(${ry}deg) translateZ(0)`;
  };
  const onLeave = () => {
    const el = ref.current; if (!el) return;
    el.style.transform = 'perspective(900px) rotateX(0deg) rotateY(0deg) translateZ(0)';
  };
  return (
    <div ref={ref} onMouseMove={onMove} onMouseLeave={onLeave}
      style={{
        position: 'relative',
        width: 400, padding: 32,
        background: 'linear-gradient(180deg, rgba(28,32,48,.85), rgba(22,25,34,.85))',
        backdropFilter: 'blur(14px) saturate(140%)',
        WebkitBackdropFilter: 'blur(14px) saturate(140%)',
        border: '1px solid rgba(120,140,200,.18)',
        borderRadius: 16,
        boxShadow:
          '0 30px 80px -20px rgba(2,6,23,.85),' +
          '0 8px 24px -8px rgba(79,142,255,.25),' +
          'inset 0 1px 0 rgba(255,255,255,.06)',
        display: 'flex', flexDirection: 'column', gap: 16,
        transformStyle: 'preserve-3d',
        transition: 'transform 240ms cubic-bezier(.2,.7,.3,1)',
      }}>
      {/* top sheen */}
      <div style={{
        position: 'absolute', top: 0, left: 0, right: 0, height: 1,
        background: 'linear-gradient(90deg, transparent, rgba(120,170,255,.35), transparent)',
        borderRadius: 16,
      }} />
      {children}
    </div>
  );
}

function _Logo() {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
      <div style={{
        width: 36, height: 36, borderRadius: 10,
        background: 'linear-gradient(135deg, #4F8EFF 0%, #2563EB 60%, #1E3A8A 100%)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        boxShadow:
          '0 6px 16px -4px rgba(79,142,255,.55),' +
          'inset 0 1px 0 rgba(255,255,255,.25),' +
          'inset 0 -1px 0 rgba(0,0,0,.25)',
        position: 'relative',
      }}>
        <div style={{
          width: 14, height: 14, borderRadius: 3,
          background: 'rgba(255,255,255,.92)',
          boxShadow: 'inset 0 -1px 0 rgba(0,0,0,.15)',
        }} />
      </div>
      <div>
        <div style={{ fontSize: 17, fontWeight: 700, letterSpacing: -0.4, color: 'var(--text-0)' }}>Workspace</div>
        <div style={{ fontSize: 11.5, color: 'var(--text-2)', marginTop: 1 }}>Vault &amp; Tracker</div>
      </div>
    </div>
  );
}

function _Field({ label, children }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      <label style={{ fontSize: 11, color: 'var(--text-2)', letterSpacing: 0.4, textTransform: 'uppercase' }}>{label}</label>
      {children}
    </div>
  );
}

function _Input({ inputRef, ...props }) {
  return (
    <input
      ref={inputRef}
      {...props}
      style={{
        width: '100%', boxSizing: 'border-box',
        padding: '10px 12px',
        background: 'var(--bg-0)',
        border: '1px solid var(--border)',
        borderRadius: 'var(--r-sm)',
        color: 'var(--text-0)',
        fontSize: 13,
        outline: 'none',
        ...(props.style || {}),
      }}
    />
  );
}

function _ErrorBox({ children }) {
  if (!children) return null;
  return (
    <div style={{
      padding: '8px 10px',
      background: 'rgba(239,68,68,.08)',
      border: '1px solid rgba(239,68,68,.30)',
      borderRadius: 'var(--r-sm)',
      color: 'var(--text-0)', fontSize: 12.5,
    }}>{children}</div>
  );
}

function _SubmitButton({ submitting, disabled, children }) {
  const isDisabled = submitting || disabled;
  return (
    <button
      type="submit"
      disabled={isDisabled}
      className="btn-reset"
      style={{
        padding: '10px 12px',
        background: isDisabled ? 'var(--bg-2)' : 'var(--text-0)',
        color: isDisabled ? 'var(--text-2)' : 'var(--bg-0)',
        border: 'none',
        borderRadius: 'var(--r-sm)',
        fontSize: 13, fontWeight: 600,
        cursor: isDisabled ? 'default' : 'pointer',
      }}>
      {children}
    </button>
  );
}

// Floating "← Volver a la landing" pill. Rendered via portal into
// document.body so it sits above every stacking context the auth shell
// creates (orbs, blur, gradient overlays).
function _BackToLandingButton({ onClick }) {
  if (typeof document === 'undefined' || !document.body) return null;
  return ReactDOM.createPortal(
    <button type="button" onClick={onClick}
      style={{
        position: 'fixed', top: 18, left: 18, zIndex: 9999,
        background: 'rgba(22,25,34,.78)',
        border: '1px solid rgba(255,255,255,.12)',
        color: 'var(--text-0)',
        padding: '8px 14px', borderRadius: 999,
        cursor: 'pointer', fontSize: 13, fontWeight: 500,
        display: 'inline-flex', alignItems: 'center', gap: 6,
        backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)',
        boxShadow: '0 4px 14px rgba(0,0,0,.35)',
        transition: 'background 160ms, border-color 160ms, transform 160ms',
      }}
      onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(36,40,52,.88)'; e.currentTarget.style.transform = 'translateY(-1px)'; }}
      onMouseLeave={(e) => { e.currentTarget.style.background = 'rgba(22,25,34,.78)'; e.currentTarget.style.transform = 'none'; }}
    >
      <span aria-hidden="true">←</span>
      Volver a la landing
    </button>,
    document.body
  );
}

function _Shell({ children }) {
  return (
    <div style={{
      minHeight: '100vh', width: '100vw',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      background:
        'radial-gradient(1200px 600px at 20% 10%, rgba(79,142,255,.12), transparent 60%),' +
        'radial-gradient(900px 500px at 85% 90%, rgba(168,85,247,.10), transparent 60%),' +
        'radial-gradient(700px 400px at 60% 50%, rgba(16,185,129,.06), transparent 60%),' +
        'var(--bg-0)',
      position: 'relative', overflow: 'hidden',
    }}>
      {/* Animated grid backdrop. Thin, low-opacity lines that drift slowly
          to give a sense of depth without distracting from the form. */}
      <div aria-hidden="true" style={{
        position: 'absolute', inset: 0,
        backgroundImage:
          'linear-gradient(rgba(120,140,200,.06) 1px, transparent 1px),' +
          'linear-gradient(90deg, rgba(120,140,200,.06) 1px, transparent 1px)',
        backgroundSize: '48px 48px',
        maskImage: 'radial-gradient(ellipse at center, black 35%, transparent 80%)',
        WebkitMaskImage: 'radial-gradient(ellipse at center, black 35%, transparent 80%)',
        animation: 'grid-drift 40s linear infinite',
        pointerEvents: 'none',
      }} />
      {/* Floating glow orbs — pure CSS, GPU-friendly. */}
      <div aria-hidden="true" style={{
        position: 'absolute', top: '-120px', left: '-120px', width: 480, height: 480,
        background: 'radial-gradient(circle, rgba(79,142,255,.35) 0%, transparent 65%)',
        filter: 'blur(20px)',
        animation: 'orb-float-a 18s ease-in-out infinite',
        pointerEvents: 'none',
      }} />
      <div aria-hidden="true" style={{
        position: 'absolute', bottom: '-180px', right: '-140px', width: 540, height: 540,
        background: 'radial-gradient(circle, rgba(168,85,247,.28) 0%, transparent 65%)',
        filter: 'blur(20px)',
        animation: 'orb-float-b 22s ease-in-out infinite',
        pointerEvents: 'none',
      }} />
      {/* Card slot */}
      <div style={{ position: 'relative', zIndex: 1 }}>{children}</div>

      {/* Floating support button — visible on every auth screen so users
          can reach us even if they can't log in. */}
      <div style={{ position: 'fixed', bottom: 24, right: 24, zIndex: 50 }}>
        <SupportButton variant="corner" />
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// LoginScreen — email + password
// ─────────────────────────────────────────────────────────────────────────

function LoginScreen({ onLoggedIn, onSwitchToSignup, onSwitchToForgot, onBackToLanding }) {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');
  const [submitting, setSubmitting] = React.useState(false);
  const [error, setError] = React.useState(null);
  const emailRef = React.useRef(null);

  React.useEffect(() => { emailRef.current && emailRef.current.focus(); }, []);

  const submit = async (e) => {
    e.preventDefault();
    if (submitting) return;
    setError(null);
    setSubmitting(true);
    try {
      await window.loginByEmail(email.trim().toLowerCase(), password);
      // If a /accept-invite/<token> bounced us here, accept now that we
      // have a session — otherwise the user would log in but not be in
      // the workspace they were invited to. Best-effort: failures clear
      // the stash so we don't loop.
      await _consumePendingInvite();
      // If `?next=` is present in the URL, honour it. This is how the
      // OAuth consent flow bounces unauth'd users through /login and back:
      // /oauth/authorize sets Location: /login?next=<encoded authorize URL>,
      // and once the credentials check passes we redirect there. Restrict
      // to same-origin paths to prevent open-redirect abuse.
      const params = new URLSearchParams(window.location.search);
      const next = params.get('next');
      if (next && /^\/[^/]/.test(next)) {
        window.location.href = next;
        return;
      }
      await window.bootApp();
      onLoggedIn && onLoggedIn();
    } catch (err) {
      setError(err.status === 401 ? 'Credenciales inválidas.' : (err.message || 'Error al iniciar sesión.'));
      setPassword('');
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <_Shell>
      {onBackToLanding && <_BackToLandingButton onClick={onBackToLanding} />}
      <form onSubmit={submit}>
        <_Card>
          <_Logo />
          <_Field label="Email">
            <_Input
              inputRef={emailRef}
              type="email" autoComplete="email"
              value={email} onChange={(e) => setEmail(e.target.value)}
              placeholder="tu@email.com"
              disabled={submitting}
            />
          </_Field>
          <_Field label="Contraseña">
            <_Input
              type="password" autoComplete="current-password"
              value={password} onChange={(e) => setPassword(e.target.value)}
              disabled={submitting}
            />
          </_Field>
          <_ErrorBox>{error}</_ErrorBox>
          <_SubmitButton submitting={submitting} disabled={!email || !password}>
            {submitting ? 'Ingresando…' : 'Iniciar sesión'}
          </_SubmitButton>
          <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: 'var(--text-2)', marginTop: 4 }}>
            <button type="button" className="btn-reset" onClick={onSwitchToForgot}
              style={{ color: 'var(--text-2)', textDecoration: 'underline', cursor: 'pointer' }}>
              Olvidé mi contraseña
            </button>
            <button type="button" className="btn-reset" onClick={onSwitchToSignup}
              style={{ color: 'var(--accent)', textDecoration: 'underline', cursor: 'pointer' }}>
              Crear cuenta
            </button>
          </div>
        </_Card>
      </form>
    </_Shell>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// ForgotPasswordScreen — request a reset email
// ─────────────────────────────────────────────────────────────────────────

function ForgotPasswordScreen({ onSwitchToLogin }) {
  const [email, setEmail] = React.useState('');
  const [submitting, setSubmitting] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [sent, setSent] = React.useState(false);

  const submit = async (e) => {
    e.preventDefault();
    if (submitting) return;
    setError(null);
    setSubmitting(true);
    try {
      await window.requestPasswordReset(email.trim().toLowerCase());
      setSent(true);
    } catch (err) {
      setError(err.message || 'Error al solicitar reset');
    } finally {
      setSubmitting(false);
    }
  };

  if (sent) {
    return (
      <_Shell>
        <_Card>
          <_Logo />
          <div style={{ marginTop: 4 }}>
            <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text-0)', marginBottom: 6 }}>Revisá tu inbox 📬</div>
            <div style={{ fontSize: 13, color: 'var(--text-1)', lineHeight: 1.55 }}>
              Si <span className="mono" style={{ color: 'var(--accent)' }}>{email}</span> está registrado, te enviamos un enlace para restablecer tu contraseña. El enlace es válido por 1 hora.
            </div>
          </div>
          <button type="button" className="btn-reset" onClick={onSwitchToLogin}
            style={{ padding: '10px 12px', background: 'var(--text-0)', color: 'var(--bg-0)', border: 'none', borderRadius: 'var(--r-sm)', fontSize: 13, fontWeight: 600, cursor: 'pointer' }}>
            Volver al login
          </button>
        </_Card>
      </_Shell>
    );
  }

  return (
    <_Shell>
      <form onSubmit={submit}>
        <_Card>
          <_Logo />
          <div style={{ fontSize: 13, color: 'var(--text-2)', lineHeight: 1.5 }}>
            Ingresá tu email y te enviamos un enlace para elegir una contraseña nueva.
          </div>
          <_Field label="Email">
            <_Input type="email" autoComplete="email" autoFocus
              value={email} onChange={(e) => setEmail(e.target.value)}
              placeholder="tu@email.com" disabled={submitting} />
          </_Field>
          <_ErrorBox>{error}</_ErrorBox>
          <_SubmitButton submitting={submitting} disabled={!email}>
            {submitting ? 'Enviando…' : 'Enviar enlace'}
          </_SubmitButton>
          <div style={{ textAlign: 'center', fontSize: 12, color: 'var(--text-2)', marginTop: 4 }}>
            <button type="button" className="btn-reset" onClick={onSwitchToLogin}
              style={{ color: 'var(--accent)', textDecoration: 'underline', cursor: 'pointer' }}>
              Volver al login
            </button>
          </div>
        </_Card>
      </form>
    </_Shell>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// ResetPasswordScreen — consume token + set new password
// Shown when URL = /reset-password?token=...
// ─────────────────────────────────────────────────────────────────────────

function ResetPasswordScreen({ token, onDone, onSwitchToLogin }) {
  const [newPassword, setNewPassword] = React.useState('');
  const [confirm, setConfirm] = React.useState('');
  const [submitting, setSubmitting] = React.useState(false);
  const [error, setError] = React.useState(null);

  const valid = newPassword.length >= 10 && newPassword === confirm;

  const submit = async (e) => {
    e.preventDefault();
    if (submitting || !valid) return;
    setError(null);
    setSubmitting(true);
    try {
      await window.confirmPasswordReset(token, newPassword);
      // Backend set the cookie — boot the app and continue.
      try {
        await window.bootData();
        onDone && onDone();
      } catch {
        onSwitchToLogin && onSwitchToLogin();
      }
    } catch (err) {
      setError(err.message || 'No se pudo resetear');
    } finally {
      setSubmitting(false);
    }
  };

  if (!token) {
    return (
      <_Shell><_Card>
        <_Logo />
        <div><div style={{ fontSize: 16, fontWeight: 600, marginBottom: 6 }}>Enlace inválido</div>
          <div style={{ fontSize: 13, color: 'var(--text-1)' }}>Este enlace no tiene token. Solicita uno nuevo desde "Olvidé mi contraseña".</div></div>
        <button type="button" className="btn-reset" onClick={onSwitchToLogin}
          style={{ padding: '10px 12px', background: 'var(--text-0)', color: 'var(--bg-0)', border: 'none', borderRadius: 'var(--r-sm)', fontSize: 13, fontWeight: 600, cursor: 'pointer' }}>
          Volver al login
        </button>
      </_Card></_Shell>
    );
  }

  return (
    <_Shell>
      <form onSubmit={submit}>
        <_Card>
          <_Logo />
          <_Field label="Nueva contraseña (mín. 10)">
            <_Input type="password" autoComplete="new-password" autoFocus
              value={newPassword} onChange={(e) => setNewPassword(e.target.value)}
              disabled={submitting} maxLength={256} />
          </_Field>
          <_Field label="Repetir contraseña">
            <_Input type="password" autoComplete="new-password"
              value={confirm} onChange={(e) => setConfirm(e.target.value)}
              disabled={submitting} maxLength={256} />
          </_Field>
          {newPassword && confirm && newPassword !== confirm && (
            <div style={{ fontSize: 12, color: 'var(--danger)' }}>Las contraseñas no coinciden.</div>
          )}
          <_ErrorBox>{error}</_ErrorBox>
          <_SubmitButton submitting={submitting} disabled={!valid}>
            {submitting ? 'Guardando…' : 'Guardar nueva contraseña'}
          </_SubmitButton>
        </_Card>
      </form>
    </_Shell>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// SignupScreen — email + name + password → "check your inbox"
// ─────────────────────────────────────────────────────────────────────────

function SignupScreen({ onSwitchToLogin }) {
  // If we got here from /accept-invite/<token> → "Crear cuenta", the invite
  // token + email are stashed in localStorage. Pre-fill the email and lock
  // it so the invitee can't sign up with a different address (which would
  // make the invite un-acceptable).
  const _pending = (typeof window !== 'undefined') ? _pendingInvite.get() : null;
  const [email, setEmail] = React.useState(_pending?.email || '');
  const [displayName, setDisplayName] = React.useState('');
  const [password, setPassword] = React.useState('');
  const [submitting, setSubmitting] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [success, setSuccess] = React.useState(null); // {emailSent, verificationUrl, email}
  const emailLocked = !!_pending?.email;

  const valid = email.trim() && displayName.trim() && password.length >= 10;

  const submit = async (e) => {
    e.preventDefault();
    if (submitting || !valid) return;
    setError(null);
    setSubmitting(true);
    try {
      const res = await window.signup(
        email.trim().toLowerCase(),
        password,
        displayName.trim(),
        // Pass the stashed invite token so the backend skips auto-stub
        // workspace creation. Falls back to the standard flow if absent.
        _pending?.token || undefined,
      );
      setSuccess({
        emailSent: res.emailSent,
        verificationUrl: res.verificationUrl,
        email: res.user.email,
      });
    } catch (err) {
      if (err.status === 409) setError('Ya existe una cuenta con ese email.');
      else if (err.status === 400) setError(err.message || 'Datos inválidos. La contraseña debe tener al menos 10 caracteres.');
      else setError(err.message || 'Error creando cuenta.');
    } finally {
      setSubmitting(false);
    }
  };

  if (success) {
    return (
      <_Shell>
        <_Card>
          <_Logo />
          <div style={{ marginTop: 4 }}>
            <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text-0)', marginBottom: 6 }}>
              Revisa tu email 📬
            </div>
            <div style={{ fontSize: 13, color: 'var(--text-1)', lineHeight: 1.55 }}>
              {success.emailSent
                ? <>Te enviamos un enlace de confirmación a <span style={{ color: 'var(--accent)' }}>{success.email}</span>. Haz clic para activar tu cuenta.</>
                : <>El driver de email está en modo dev (stdout). Abre esta URL para completar la verificación:</>
              }
            </div>
          </div>
          {!success.emailSent && success.verificationUrl && (
            <div style={{
              padding: '10px 12px',
              background: 'var(--bg-2)',
              border: '1px solid var(--border)',
              borderRadius: 'var(--r-sm)',
              fontSize: 11,
              fontFamily: 'var(--font-mono)',
              color: 'var(--text-1)',
              wordBreak: 'break-all',
            }}>
              <a href={success.verificationUrl} style={{ color: 'var(--accent)' }}>{success.verificationUrl}</a>
            </div>
          )}
          <button type="button" className="btn-reset" onClick={onSwitchToLogin}
            style={{
              padding: '10px 12px',
              background: 'transparent',
              color: 'var(--text-1)',
              border: '1px solid var(--border)',
              borderRadius: 'var(--r-sm)',
              fontSize: 13, fontWeight: 500,
              cursor: 'pointer',
            }}>
            Volver al login
          </button>
        </_Card>
      </_Shell>
    );
  }

  return (
    <_Shell>
      <form onSubmit={submit}>
        <_Card>
          <_Logo />
          {emailLocked && (
            <div style={{
              padding: '10px 12px',
              background: 'rgba(79,142,255,.08)',
              border: '1px solid rgba(79,142,255,.30)',
              borderRadius: 'var(--r-sm)',
              fontSize: 12.5, color: 'var(--text-1)', lineHeight: 1.5,
            }}>
              Te invitaron a un workspace. Crea tu cuenta con <span className="mono" style={{ color: 'var(--accent)' }}>{email}</span> y te metemos automáticamente al aceptar.
            </div>
          )}
          <_Field label="Tu nombre">
            <_Input type="text" autoComplete="name"
              value={displayName} onChange={(e) => setDisplayName(e.target.value)}
              placeholder="Sebastián Castillo"
              disabled={submitting} maxLength={80} />
          </_Field>
          <_Field label="Email">
            <_Input type="email" autoComplete="email"
              value={email} onChange={(e) => emailLocked ? null : setEmail(e.target.value)}
              placeholder="tu@email.com"
              disabled={submitting || emailLocked} maxLength={254}
              readOnly={emailLocked} />
          </_Field>
          <_Field label="Contraseña (mín. 10 caracteres)">
            <_Input type="password" autoComplete="new-password"
              value={password} onChange={(e) => setPassword(e.target.value)}
              disabled={submitting} maxLength={256} />
          </_Field>
          <_ErrorBox>{error}</_ErrorBox>
          <_SubmitButton submitting={submitting} disabled={!valid}>
            {submitting ? 'Creando cuenta…' : 'Crear cuenta'}
          </_SubmitButton>
          <div style={{ display: 'flex', justifyContent: 'center', fontSize: 12, color: 'var(--text-2)', marginTop: 4 }}>
            ¿Ya tienes cuenta?{' '}
            <button type="button" className="btn-reset" onClick={onSwitchToLogin}
              style={{ marginLeft: 6, color: 'var(--accent)', textDecoration: 'underline', cursor: 'pointer' }}>
              Iniciar sesión
            </button>
          </div>
        </_Card>
      </form>
    </_Shell>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// VerifyEmailLanding — shown when GET /api/auth/verify-email redirects
// ─────────────────────────────────────────────────────────────────────────

function VerifyEmailLanding({ status, onContinue }) {
  // Race-condition safety net: when status is unknown (URL stripped of
  // ?status by an email client / privacy extension / paste mishap), check
  // whether the cookie was actually set by /api/auth/verify-email. If
  // /api/me succeeds, verification did happen and we should escort the
  // user into the app instead of telling them the link is broken.
  const [resolvedStatus, setResolvedStatus] = React.useState(status);
  React.useEffect(() => {
    const known = status === 'ok' || status === 'expired' || status === 'used';
    if (known) return;
    // Unknown status — probe /api/me. If we have a session, treat this as ok.
    let cancelled = false;
    fetch('/api/me', { credentials: 'include' })
      .then((r) => { if (!cancelled && r.ok) setResolvedStatus('ok'); })
      .catch(() => {});
    return () => { cancelled = true; };
  }, [status]);

  const meta = (() => {
    if (resolvedStatus === 'ok')      return { title: 'Email confirmado ✓',  sub: 'Tu cuenta está activa. Ya puedes entrar al workspace.' };
    if (resolvedStatus === 'expired') return { title: 'Enlace expirado',     sub: 'El enlace de verificación ya venció. Solicita uno nuevo desde el login.' };
    if (resolvedStatus === 'used')    return { title: 'Enlace ya usado',     sub: 'Este enlace ya se usó una vez. Si tu cuenta está activa, simplemente inicia sesión.' };
    return                                  { title: 'Enlace inválido',     sub: 'No pudimos validar el enlace. Si ya creaste tu cuenta, intenta iniciar sesión normalmente.' };
  })();
  return (
    <_Shell>
      <_Card>
        <_Logo />
        <div style={{ marginTop: 4 }}>
          <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text-0)', marginBottom: 6 }}>{meta.title}</div>
          <div style={{ fontSize: 13, color: 'var(--text-1)', lineHeight: 1.55 }}>{meta.sub}</div>
        </div>
        <button type="button" className="btn-reset" onClick={() => onContinue && onContinue(resolvedStatus)}
          style={{
            padding: '10px 12px',
            background: 'var(--text-0)',
            color: 'var(--bg-0)',
            border: 'none',
            borderRadius: 'var(--r-sm)',
            fontSize: 13, fontWeight: 600,
            cursor: 'pointer',
          }}>
          {resolvedStatus === 'ok' ? 'Entrar al workspace' : 'Iniciar sesión'}
        </button>
      </_Card>
    </_Shell>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// AcceptInviteScreen — preview invite + accept flow
// Shown when URL = /accept-invite/<token>. Works for both logged-in and
// anonymous users; the latter get a login/signup nudge first.
// ─────────────────────────────────────────────────────────────────────────

function AcceptInviteScreen({ token, onSwitchToLogin, onSwitchToSignup, onAccepted }) {
  const [preview, setPreview] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [accepting, setAccepting] = React.useState(false);

  React.useEffect(() => {
    (async () => {
      try {
        const p = await window.api('GET', `/api/invites/${encodeURIComponent(token)}`);
        setPreview(p);
      } catch (e) {
        setError(e?.message || 'Invitación no válida');
      }
    })();
  }, [token]);

  const accept = async () => {
    setAccepting(true);
    setError(null);
    try {
      await window.api('POST', `/api/invites/${encodeURIComponent(token)}/accept`);
      onAccepted && onAccepted();
    } catch (e) {
      setError(e?.message || 'No se pudo aceptar');
      setAccepting(false);
    }
  };

  if (error && !preview) {
    return (
      <_Shell>
        <_Card>
          <_Logo />
          <div style={{ marginTop: 4 }}>
            <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text-0)', marginBottom: 6 }}>Invitación inválida</div>
            <div style={{ fontSize: 13, color: 'var(--text-1)', lineHeight: 1.55 }}>{error}</div>
          </div>
          <button type="button" className="btn-reset" onClick={onSwitchToLogin}
            style={{ padding: '10px 12px', background: 'var(--text-0)', color: 'var(--bg-0)', border: 'none', borderRadius: 'var(--r-sm)', fontSize: 13, fontWeight: 600, cursor: 'pointer' }}>
            Volver al login
          </button>
        </_Card>
      </_Shell>
    );
  }
  if (!preview) {
    return <_Shell><_Card><_Logo /><div style={{ color: 'var(--text-2)', fontSize: 13 }}>Cargando invitación…</div></_Card></_Shell>;
  }
  if (preview.expired) {
    return (
      <_Shell><_Card>
        <_Logo />
        <div><div style={{ fontSize: 16, fontWeight: 600, marginBottom: 6 }}>Invitación expirada</div>
          <div style={{ fontSize: 13, color: 'var(--text-1)' }}>El enlace expiró el {new Date(preview.expiresAt).toLocaleDateString('es-MX')}. Pídele a {preview.inviter.displayName} que te envíe otro.</div></div>
      </_Card></_Shell>
    );
  }
  if (preview.accepted) {
    return (
      <_Shell><_Card>
        <_Logo />
        <div><div style={{ fontSize: 16, fontWeight: 600, marginBottom: 6 }}>Invitación ya aceptada</div>
          <div style={{ fontSize: 13, color: 'var(--text-1)' }}>Esta invitación ya se usó. Si tu cuenta está activa, iniciá sesión normalmente.</div></div>
        <button type="button" className="btn-reset" onClick={onSwitchToLogin}
          style={{ padding: '10px 12px', background: 'var(--text-0)', color: 'var(--bg-0)', border: 'none', borderRadius: 'var(--r-sm)', fontSize: 13, fontWeight: 600, cursor: 'pointer' }}>
          Iniciar sesión
        </button>
      </_Card></_Shell>
    );
  }

  const me = window.ME;
  const sameEmail = me && me.email && me.email.toLowerCase() === preview.email.toLowerCase();

  return (
    <_Shell>
      <_Card>
        <_Logo />
        <div style={{ marginTop: 4 }}>
          <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--text-0)', marginBottom: 6 }}>Te invitaron a colaborar</div>
          <div style={{ fontSize: 13, color: 'var(--text-1)', lineHeight: 1.55 }}>
            <strong style={{ color: 'var(--text-0)' }}>{preview.inviter.displayName}</strong> ({preview.inviter.email})
            te invitó {preview.project
              ? <>al proyecto <strong style={{ color: 'var(--text-0)' }}>{preview.project.name}</strong> dentro del workspace <strong style={{ color: 'var(--text-0)' }}>{preview.workspace.name}</strong></>
              : <>al workspace <strong style={{ color: 'var(--text-0)' }}>{preview.workspace.name}</strong></>
            } como <strong style={{ color: 'var(--accent)' }}>{preview.role.name}</strong>.
          </div>
        </div>
        <div style={{
          padding: '10px 12px',
          background: 'var(--bg-2)', border: '1px solid var(--border)',
          borderRadius: 'var(--r-sm)', fontSize: 12, color: 'var(--text-2)',
        }}>
          Email: <span className="mono" style={{ color: 'var(--text-1)' }}>{preview.email}</span>
        </div>

        {error && <_ErrorBox>{error}</_ErrorBox>}

        {!me && (
          <>
            <div style={{ fontSize: 12, color: 'var(--text-2)' }}>Para aceptar, inicia sesión o crea una cuenta con ese email.</div>
            <div style={{ display: 'flex', gap: 8 }}>
              <button type="button" className="btn-reset"
                onClick={() => { _pendingInvite.set(token, preview.email); onSwitchToLogin && onSwitchToLogin(); }}
                style={{ flex: 1, padding: '10px 12px', background: 'transparent', color: 'var(--text-1)', border: '1px solid var(--border)', borderRadius: 'var(--r-sm)', fontSize: 13, fontWeight: 500, cursor: 'pointer' }}>
                Tengo cuenta
              </button>
              <button type="button" className="btn-reset"
                onClick={() => { _pendingInvite.set(token, preview.email); onSwitchToSignup && onSwitchToSignup(); }}
                style={{ flex: 1, padding: '10px 12px', background: 'var(--text-0)', color: 'var(--bg-0)', border: 'none', borderRadius: 'var(--r-sm)', fontSize: 13, fontWeight: 600, cursor: 'pointer' }}>
                Crear cuenta
              </button>
            </div>
          </>
        )}

        {me && !sameEmail && (
          <>
            <div style={{ fontSize: 12, color: 'var(--danger)' }}>
              Estás logueado como <span className="mono">{me.email || me.name}</span>, pero esta invitación es para <span className="mono">{preview.email}</span>.
              Cierra sesión e inicia sesión con la cuenta correcta.
            </div>
            <button type="button" className="btn-reset" onClick={async () => { await window.logout(); window.location.reload(); }}
              style={{ padding: '10px 12px', background: 'var(--text-0)', color: 'var(--bg-0)', border: 'none', borderRadius: 'var(--r-sm)', fontSize: 13, fontWeight: 600, cursor: 'pointer' }}>
              Cerrar sesión
            </button>
          </>
        )}

        {me && sameEmail && (
          <button type="button" className="btn-reset" onClick={accept} disabled={accepting}
            style={{ padding: '10px 12px', background: accepting ? 'var(--bg-2)' : 'var(--text-0)', color: accepting ? 'var(--text-2)' : 'var(--bg-0)', border: 'none', borderRadius: 'var(--r-sm)', fontSize: 13, fontWeight: 600, cursor: accepting ? 'default' : 'pointer' }}>
            {accepting ? 'Aceptando…' : `Aceptar invitación`}
          </button>
        )}
      </_Card>
    </_Shell>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// AuthGate — controls which screen to show when window.ME is null
// ─────────────────────────────────────────────────────────────────────────

function _detectInitialMode() {
  if (typeof window === 'undefined') return { mode: 'landing' };
  const path = window.location.pathname || '/';
  if (path === '/verify-email') return { mode: 'verify-email' };
  if (path === '/reset-password') {
    const params = new URLSearchParams(window.location.search);
    return { mode: 'reset-password', token: params.get('token') };
  }
  if (path === '/forgot-password') return { mode: 'forgot-password' };
  if (path === '/signup') return { mode: 'signup' };
  if (path === '/login') return { mode: 'login' };
  if (path.startsWith('/accept-invite/')) {
    const token = decodeURIComponent(path.slice('/accept-invite/'.length));
    return { mode: 'accept-invite', token };
  }
  // Default for "/" (logged-out visitors) → marketing landing. The legacy
  // login form lives at /login and is reachable from the landing's CTAs.
  return { mode: 'landing' };
}

function AuthGate({ onLoggedIn }) {
  const initial = _detectInitialMode();
  const [mode, setMode] = React.useState(initial.mode);
  const [inviteToken] = React.useState(initial.mode === 'accept-invite' ? initial.token : null);
  const [resetToken] = React.useState(initial.mode === 'reset-password' ? initial.token : null);

  if (mode === 'verify-email') {
    const params = new URLSearchParams(window.location.search);
    // Empty/missing status means "unknown"; VerifyEmailLanding will probe
    // /api/me to decide whether the user actually got verified.
    const status = params.get('status') || '';
    return (
      <VerifyEmailLanding
        status={status}
        onContinue={(resolved) => {
          // `resolved` reflects the outcome of the in-component probe —
          // can be 'ok' even if the URL didn't carry it.
          const effective = resolved || status;
          window.history.replaceState({}, '', '/');
          if (effective === 'ok') {
            (async () => {
              try {
                // If signup was triggered from an invite, consume it now
                // that the email is verified and the cookie is set.
                await _consumePendingInvite();
                await window.bootData();
                onLoggedIn && onLoggedIn();
              } catch { setMode('login'); }
            })();
          } else {
            setMode('login');
          }
        }}
      />
    );
  }

  if (mode === 'accept-invite' && inviteToken) {
    return (
      <AcceptInviteScreen
        token={inviteToken}
        onSwitchToLogin={() => setMode('login')}
        onSwitchToSignup={() => setMode('signup')}
        onAccepted={() => {
          window.history.replaceState({}, '', '/');
          (async () => {
            // Trigger a re-boot so window.ME picks up the new workspace membership.
            try { await window.bootApp(); onLoggedIn && onLoggedIn(); }
            catch { setMode('login'); }
          })();
        }}
      />
    );
  }

  if (mode === 'forgot-password') return <ForgotPasswordScreen onSwitchToLogin={() => { window.history.replaceState({}, '', '/'); setMode('login'); }} />;
  if (mode === 'reset-password') {
    return (
      <ResetPasswordScreen
        token={resetToken}
        onDone={() => { window.history.replaceState({}, '', '/'); onLoggedIn && onLoggedIn(); }}
        onSwitchToLogin={() => { window.history.replaceState({}, '', '/'); setMode('login'); }}
      />
    );
  }
  if (mode === 'signup') return <SignupScreen onSwitchToLogin={() => { window.history.pushState({}, '', '/login'); setMode('login'); }} />;
  if (mode === 'landing' && window.LandingScreen) {
    return <window.LandingScreen
      onGoLogin={() => { window.history.pushState({}, '', '/login'); setMode('login'); }}
      onGoSignup={() => { window.history.pushState({}, '', '/signup'); setMode('signup'); }}
    />;
  }
  return <LoginScreen
    onLoggedIn={onLoggedIn}
    onSwitchToSignup={() => { window.history.pushState({}, '', '/signup'); setMode('signup'); }}
    onSwitchToForgot={() => { window.history.pushState({}, '', '/forgot-password'); setMode('forgot-password'); }}
    onBackToLanding={() => { window.history.pushState({}, '', '/'); setMode('landing'); }}
  />;
}

window.LoginScreen = LoginScreen;
window.SignupScreen = SignupScreen;
window.VerifyEmailLanding = VerifyEmailLanding;
window.AcceptInviteScreen = AcceptInviteScreen;
window.ForgotPasswordScreen = ForgotPasswordScreen;
window.ResetPasswordScreen = ResetPasswordScreen;
window.AuthGate = AuthGate;
