// @File: src/lib/auth-api.ts
// @Desc: Self-contained auth helpers that talk directly to our FastAPI backend
//        without going through the @metagptx/web-sdk. This is important because
//        the web-sdk's default behaviour on a 401 is to redirect to the Atoms
//        OIDC login page (`/api/v1/auth/login`), which would yank the user out
//        of our standalone email/password flow and bounce them back to Atoms.
//
//        By using plain `fetch` here, we get to decide exactly what happens on
//        auth failures: clear the token, stay on /login, and never redirect to
//        the Atoms OIDC endpoint.
import { clearSession, getToken } from "@/lib/auth";

export interface MeResponse {
  id: string;
  email: string;
  name?: string;
  role?: string;
}

export class AuthUnauthorizedError extends Error {
  constructor(message = "Unauthorized") {
    super(message);
    this.name = "AuthUnauthorizedError";
  }
}

export class AuthTransientError extends Error {
  status?: number;
  constructor(message: string, status?: number) {
    super(message);
    this.name = "AuthTransientError";
    this.status = status;
  }
}

/**
 * Call the backend `/api/v1/auth/me` endpoint directly with our JWT.
 *
 * - Returns the user object on 200.
 * - Throws `AuthUnauthorizedError` on 401 (caller should sign out locally).
 * - Throws `AuthTransientError` on 5xx / network errors (caller may retry).
 *
 * Crucially, this function NEVER redirects the browser. The decision of where
 * to send an unauthenticated user belongs to the page, not the transport layer.
 */
export async function fetchMeDirect(): Promise<MeResponse> {
  const token = getToken();
  if (!token) {
    throw new AuthUnauthorizedError("No session token stored.");
  }

  let res: Response;
  try {
    res = await fetch("/api/v1/auth/me", {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
      },
      // We never want the browser to follow any 3xx auth redirect — our JWT
      // is self-contained and the backend never issues redirects for /auth/me.
      redirect: "manual",
      credentials: "omit",
    });
  } catch (err) {
    // Network failure (DNS / LB / offline). Treat as transient so callers
    // can retry instead of signing the user out.
    throw new AuthTransientError(
      err instanceof Error ? err.message : "Network error",
    );
  }

  if (res.status === 401) {
    // Our stored token is invalid or expired. Clear it so we don't keep retrying.
    clearSession();
    throw new AuthUnauthorizedError();
  }

  if (res.status >= 500 || res.status === 0) {
    throw new AuthTransientError(`Backend returned ${res.status}`, res.status);
  }

  if (!res.ok) {
    // 4xx other than 401 — treat as unauthenticated to be safe.
    throw new AuthUnauthorizedError(`Unexpected ${res.status}`);
  }

  const body = await res.json().catch(() => null);
  if (!body || !body.id) {
    throw new AuthUnauthorizedError("Malformed /auth/me response.");
  }
  return body as MeResponse;
}