import { resolveApiUrl } from "../../lib/queryClient";

export type AdminAccessState = "allowed" | "unauthenticated" | "forbidden" | "unavailable";
export type AdminErrorKind =
  | "unauthorized"
  | "forbidden"
  | "not_found"
  | "server"
  | "unavailable"
  | "network"
  | "unexpected";

type AdminRequestOptions = {
  method?: string;
  body?: unknown;
  treat404AsNull?: boolean;
};

function looksLikeHtml(value: string) {
  return /<!doctype html|<html[\s>]|<head[\s>]|<body[\s>]/i.test(value);
}

export class AdminApiError extends Error {
  kind: AdminErrorKind;
  status?: number;

  constructor(kind: AdminErrorKind, message: string, status?: number) {
    super(message);
    this.kind = kind;
    this.status = status;
  }
}

export function getAdminErrorMessage(error: unknown, fallback = "Admin request failed.") {
  if (error instanceof AdminApiError) {
    switch (error.kind) {
      case "unauthorized":
        return "Session expired. Log in again.";
      case "forbidden":
        return "Admin access required.";
      case "unavailable":
        return "Admin service unavailable.";
      case "network":
        return "Cannot reach server.";
      case "not_found":
        return "Requested admin resource was not found.";
      case "server":
        return "Admin request failed.";
      default:
        return error.message || fallback;
    }
  }

  if (error instanceof Error && error.message) {
    return error.message;
  }

  return fallback;
}

export function getAdminAccessState(error: unknown): AdminAccessState {
  if (error instanceof AdminApiError) {
    if (error.kind === "unauthorized") {
      return "unauthenticated";
    }
    if (error.kind === "forbidden") {
      return "forbidden";
    }
  }
  return "unavailable";
}

export async function fetchAdminJson<T>(path: string, options: AdminRequestOptions = {}): Promise<T | null> {
  const method = options.method || "GET";

  let response: Response;
  try {
    response = await fetch(resolveApiUrl(path), {
      method,
      credentials: "include",
      headers: {
        Accept: "application/json",
        ...(options.body !== undefined ? { "Content-Type": "application/json" } : {}),
      },
      body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
    });
  } catch {
    throw new AdminApiError("network", "Cannot reach server.");
  }

  const contentType = (response.headers.get("content-type") || "").toLowerCase();
  const bodyText = response.status === 204 ? "" : (await response.clone().text()).trim();

  if (response.status === 401) {
    throw new AdminApiError("unauthorized", "Session expired. Log in again.", 401);
  }
  if (response.status === 403) {
    throw new AdminApiError("forbidden", "Admin access required.", 403);
  }
  if (response.status === 404 && options.treat404AsNull) {
    return null;
  }
  if (response.status === 404) {
    throw new AdminApiError("not_found", "Requested admin resource was not found.", 404);
  }
  if (response.status >= 500) {
    throw new AdminApiError("unavailable", "Admin service unavailable.", response.status);
  }
  if (!response.ok) {
    throw new AdminApiError("server", bodyText || "Admin request failed.", response.status);
  }
  if (bodyText && looksLikeHtml(bodyText)) {
    throw new AdminApiError("unavailable", "Admin service unavailable.", response.status);
  }
  if (!contentType.includes("application/json")) {
    if (!bodyText) {
      return null;
    }
    throw new AdminApiError("unexpected", "Unexpected admin response.", response.status);
  }

  return (await response.json()) as T;
}
