/* global React, Button, LoadingSpinner, Icon, apiFetch, formatDateTime, setAuthNextPath, useAuth */

function inviteErrorMessage(error) {
  if (!error) return '';
  if (error.code === 'membership_already_exists') {
    return 'This account already belongs to another workspace.';
  }
  if (error.code === 'invitation_expired') {
    return 'This invite has expired.';
  }
  if (error.code === 'invitation_unavailable') {
    return 'This invite is no longer available.';
  }
  if (error.code === 'invitation_email_mismatch') {
    return 'This invite is bound to a different email address.';
  }
  if (error.code === 'invitation_email_unverified') {
    return 'Verify your email address before accepting this invite.';
  }
  if (error.code === 'unauthenticated' || error.status === 401) {
    return 'Sign in again to accept this invite.';
  }
  return error.message || 'Invite could not be accepted.';
}

function InvitePage({ token, navigate, acceptRedirect = '/onboarding/role', autoRedirectOnAccept = false }) {
  const auth = useAuth();
  const [preview, setPreview] = React.useState(null);
  const [previewLoading, setPreviewLoading] = React.useState(true);
  const [previewError, setPreviewError] = React.useState(null);
  const [accepting, setAccepting] = React.useState(false);
  const [accepted, setAccepted] = React.useState(null);
  const [acceptError, setAcceptError] = React.useState(null);
  const acceptAttemptedRef = React.useRef('');
  const nextPath = `/invite/${token}/accept`;
  const isUnauthenticatedAuthError = auth?.error?.code === 'unauthenticated' || auth?.error?.status === 401;
  const hasValidSession = Boolean(auth?.session && !isUnauthenticatedAuthError);
  const hasSession = Boolean(auth?.me || hasValidSession || auth?.error?.code === 'access_not_configured');
  const canUseSupabase = Boolean(auth?.config?.authConfigured && auth?.client);

  React.useEffect(() => {
    let cancelled = false;
    acceptAttemptedRef.current = '';
    setAccepted(null);
    setAcceptError(null);
    setAccepting(false);
    async function loadPreview() {
      setPreviewLoading(true);
      setPreviewError(null);
      try {
        const result = await apiFetch(`/api/invitations/${token}`);
        if (!cancelled) setPreview(result);
      } catch (error) {
        if (!cancelled) setPreviewError(error);
      } finally {
        if (!cancelled) setPreviewLoading(false);
      }
    }
    loadPreview();
    return () => {
      cancelled = true;
    };
  }, [token]);

  React.useEffect(() => {
    if (auth?.loading || previewLoading || accepted || acceptError) return;
    const shouldAttemptAccept = hasSession &&
      (preview?.status === 'active' || (autoRedirectOnAccept && preview?.status === 'unavailable'));
    if (!shouldAttemptAccept) return;
    if (acceptAttemptedRef.current === token) return;

    let cancelled = false;
    acceptAttemptedRef.current = token;
    async function acceptInvite() {
      setAccepting(true);
      setAcceptError(null);
      try {
        const result = await apiFetch(`/api/invitations/${token}/accept`, { method: 'POST' });
        // The accept response mirrors /api/me's shape, so we hydrate auth.me
        // directly from it. We deliberately do NOT follow up with a GET
        // /api/me here: production has been observed to return 403
        // access_not_configured for ~hundreds of ms after the membership row
        // is committed (Supavisor connection-mode reuse on Vercel; the
        // pooled connection serving /api/me hasn't picked up the new row
        // yet). Hydrating from the accept response avoids the race entirely.
        auth.applyMembershipResponse(result, { flush: true });
        if (autoRedirectOnAccept) {
          navigate(acceptRedirect);
          return;
        }
        if (!cancelled) {
          setAccepted(result);
        }
      } catch (error) {
        if (!cancelled) {
          acceptAttemptedRef.current = '';
          setAcceptError(error);
        }
      } finally {
        if (!cancelled) setAccepting(false);
      }
    }
    acceptInvite();
    return () => {
      cancelled = true;
    };
  }, [auth?.loading, previewLoading, hasSession, preview?.status, accepted, acceptError, token]);

  function startEmailAuth() {
    setAuthNextPath(nextPath);
    navigate(`/signup?invite=${token}`);
  }

  async function startGoogleAuth() {
    setAuthNextPath(nextPath);
    await auth.signInWithGoogle({ nextPath });
  }

  let body;
  if (previewLoading) {
    body = (
      <div className="code inline-loading">
        <LoadingSpinner size="sm" label="Loading invite" />
        Loading invite...
      </div>
    );
  } else if (previewError) {
    body = <div className="auth-message">{previewError.message || 'Invite could not be loaded.'}</div>;
  } else if (accepting) {
    body = (
      <div className="code inline-loading">
        <LoadingSpinner size="sm" label="Accepting invite" />
        Joining workspace...
      </div>
    );
  } else if (accepted) {
    body = (
      <div className="auth-form">
        <div className="invite-success">
          <Icon name="check" size={18} />
          <span>Joined {accepted.organization?.name || preview.organization?.name || 'workspace'}.</span>
        </div>
        <Button variant="primary" className="auth-submit" onClick={() => navigate(acceptRedirect)}>
          <Icon name="activity" size={13} />Continue
        </Button>
      </div>
    );
  } else if (acceptError) {
    body = (
      <div className="auth-form">
        <div className="auth-message">{inviteErrorMessage(acceptError)}</div>
        <Button size="sm" onClick={() => auth.signOut()}>Use another session</Button>
      </div>
    );
  } else if (preview?.status === 'expired') {
    body = <div className="auth-message">This invite expired {formatDateTime(preview.expiresAt)}.</div>;
  } else if (preview?.status !== 'active') {
    body = <div className="auth-message">This invite is no longer available.</div>;
  } else if (!hasSession) {
    body = (
      <div className="auth-form">
        {canUseSupabase && (
          <Button className="auth-oauth" onClick={startGoogleAuth}>
            <img className="google-mark" src="/frontend/assets/google-g.png" alt="" aria-hidden="true" />Continue with Google
          </Button>
        )}
        <Button variant="primary" className="auth-submit" onClick={startEmailAuth}>
          <Icon name="key" size={13} />Sign in or create account
        </Button>
      </div>
    );
  }

  return (
    <div className="auth-page">
      <div className="auth-panel invite-panel">
        <div className="auth-logo">
          <img src="/frontend/assets/armature-wordmark-dark.svg" alt="Armature" />
        </div>
        <h1>{preview?.organization?.name ? `Join ${preview.organization.name}` : 'Workspace invite'}</h1>
        {preview?.status === 'active' ? (
          <div className="invite-summary">
            <div>
              <span className="field-label">Role</span>
              <span className="badge badge-neutral">{preview.role}</span>
            </div>
            <div>
              <span className="field-label">Expires</span>
              <span>{formatDateTime(preview.expiresAt)}</span>
            </div>
            {preview.boundToEmail && (
              <div>
                <span className="field-label">Email</span>
                <span>Specific account</span>
              </div>
            )}
          </div>
        ) : (
          <p className="muted">Open a valid invite to join a workspace.</p>
        )}
        {body}
      </div>
      <a className="auth-help" href="mailto:help@armature.tech">
        <Icon name="mail" size={13} />Stuck? Email us
      </a>
    </div>
  );
}

window.InvitePage = InvitePage;
