// @File: src/lib/auth.ts
// @Desc: Thin helpers for the standalone email/password auth flow. We store the
//        application JWT in localStorage under the key `token`, which is the same
//        key the @metagptx/web-sdk reads to attach `Authorization: Bearer <token>`
//        on every request. This lets the rest of the app keep using the SDK
//        (client.auth.me(), client.apiCall.invoke, client.storage, ...) unchanged.
import { client } from "@/lib/api";

const TOKEN_KEY = "token";
const MANUAL_LOGOUT_KEY = "isLougOutManual"; // matches the web-sdk's internal flag spelling

export interface StoredSession {
  token: string;
  user_id: string;
  email: string;
  name?: string | null;
  client_id: number;
}

function safeStorage(): Storage | null {
  try {
    return typeof window !== "undefined" ? window.localStorage : null;
  } catch {
    return null;
  }
}

export function getToken(): string | null {
  return safeStorage()?.getItem(TOKEN_KEY) ?? null;
}

export function setSession(session: StoredSession): void {
  const storage = safeStorage();
  if (!storage) return;
  storage.setItem(TOKEN_KEY, session.token);
  // Clear the web-sdk's "manually logged out" flag so it stops suppressing auth.
  storage.setItem(MANUAL_LOGOUT_KEY, "false");
}

export function clearSession(): void {
  const storage = safeStorage();
  if (!storage) return;
  storage.removeItem(TOKEN_KEY);
  storage.setItem(MANUAL_LOGOUT_KEY, "true");
}

/** Returns true if the browser currently holds an app JWT. */
export function hasToken(): boolean {
  const t = getToken();
  return !!t && t.length > 0;
}

/**
 * Sign the user out locally (no backend call required — JWTs are stateless).
 * Clears the token and redirects to /login.
 */
export function signOut(redirect = "/login"): void {
  clearSession();
  if (typeof window !== "undefined") {
    window.location.href = redirect;
  }
}

/**
 * Convenience: fetch the current user from our backend directly. Returns null
 * if unauthenticated or the request fails. We deliberately bypass the web-sdk
 * here so a 401 does NOT trigger an automatic redirect to the Atoms OIDC
 * login page — the standalone portal is responsible for its own routing.
 */
export async function fetchMe(): Promise<{
  id: string;
  email: string;
  name?: string;
  role?: string;
} | null> {
  if (!hasToken()) return null;
  try {
    // Import lazily to avoid a circular dependency at module-evaluation time.
    const { fetchMeDirect } = await import("@/lib/auth-api");
    return await fetchMeDirect();
  } catch {
    return null;
  }
}

/**
 * Extract a human-readable error message from an axios/web-sdk error.
 *
 * Handles several tricky cases:
 *  - Backend returns an HTML 503 page (platform "Temporarily Unavailable"):
 *    the raw HTML must never be shown to the user.
 *  - Backend returns a JSON `{ detail: "..." }` payload on 4xx.
 *  - Network / transient errors with no response.
 */
export function extractErrorMessage(err: unknown, fallback: string): string {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const anyErr = err as any;
  const status: number | undefined =
    anyErr?.response?.status ?? anyErr?.status;
  const rawData = anyErr?.response?.data ?? anyErr?.data;
  const msg: unknown = anyErr?.message;

  // 1) Server-side unavailability — never leak the HTML error page.
  const looksLikeHtml =
    typeof rawData === "string" && /<!doctype html|<html/i.test(rawData);

  // 2) Structured JSON error body — prefer the backend's own `detail` message,
  //    even on 5xx responses, because our FastAPI layer deliberately surfaces
  //    actionable details (e.g. "WHMCS IP not whitelisted", "Invalid email or
  //    password"). Only fall back to the generic "temporarily unavailable"
  //    string when the server returned no useful detail at all (e.g. an HTML
  //    error page from the platform edge).
  if (rawData && typeof rawData === "object") {
    const detail = (rawData as { detail?: unknown }).detail;
    if (typeof detail === "string" && detail.trim()) return detail;
    if (detail && typeof detail === "object") {
      try {
        return JSON.stringify(detail);
      } catch {
        /* fall through */
      }
    }
  }

  if (status === 503 || status === 502 || status === 504 || looksLikeHtml) {
    return "Service is temporarily unavailable. Please try again in a moment.";
  }

  // 3) Plain string body (and not HTML) — only trust short messages.
  if (typeof rawData === "string" && rawData.trim() && !looksLikeHtml) {
    const trimmed = rawData.trim();
    if (trimmed.length < 300) return trimmed;
  }

  // 4) Fallback to the error.message if it's meaningful.
  if (typeof msg === "string" && msg.trim() && msg !== "Network Error") {
    return msg;
  }
  if (msg === "Network Error") {
    return "Unable to reach the server. Please check your connection and try again.";
  }

  return fallback;
}