/**
 * Shared SWR fetcher with standard error handling.
 */

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

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

function extractErrorMessage(payload: unknown): string | null {
  if (
    payload &&
    typeof payload === "object" &&
    "error" in payload &&
    typeof payload.error === "string"
  ) {
    return payload.error;
  }
  if (typeof payload === "string" && payload.trim()) {
    return payload;
  }
  return null;
}

async function parsePayload(res: Response): Promise<unknown> {
  if (res.status === 204) return undefined;
  if (typeof res.text === "function") {
    const text = await res.text();
    if (!text) return undefined;
    try {
      return JSON.parse(text);
    } catch {
      return text;
    }
  }
  if (typeof res.json === "function") {
    try {
      return await res.json();
    } catch {
      return undefined;
    }
  }
  return undefined;
}

type RequestJsonInit<TBody> = Omit<RequestInit, "body"> & {
  body?: TBody;
};

/**
 * Shared JSON requester for imperative client-side API calls.
 * - Parses JSON/text payloads automatically
 * - Throws ApiRequestError with normalized message on non-2xx
 */
export async function requestJson<TResponse = unknown, TBody = unknown>(
  url: string,
  init: RequestJsonInit<TBody> = {},
): Promise<TResponse> {
  const { body, headers, ...rest } = init;
  const hasBody = body !== undefined;

  const mergedHeaders = new Headers(headers);
  if (hasBody && !mergedHeaders.has("Content-Type")) {
    mergedHeaders.set("Content-Type", "application/json");
  }

  const res = await fetch(url, {
    ...rest,
    headers: mergedHeaders,
    body: hasBody ? JSON.stringify(body) : undefined,
  });

  const payload = await parsePayload(res);
  if (!res.ok) {
    const message = extractErrorMessage(payload) ?? "请求失败";
    throw new ApiRequestError(message, res.status, payload);
  }

  return payload as TResponse;
}

export async function fetcher<T = unknown>(url: string): Promise<T> {
  const res = await fetch(url);
  if (!res.ok) {
    throw new FetchError(
      res.status === 404 ? "NOT_FOUND" : res.status === 401 ? "UNAUTHORIZED" : "FETCH_ERROR",
      res.status,
    );
  }
  return res.json();
}
