export type ApiErrorCode =
  | "validation_error"
  | "not_found"
  | "database_unavailable"
  | "conflict"
  | "network_error"
  | "unknown_error";

export interface ApiErrorPayload {
  error?: string;
  details?: string[];
}

export class ApiError extends Error {
  public readonly code: ApiErrorCode;
  public readonly status: number | null;
  public readonly details: string[];

  constructor(params: {
    code: ApiErrorCode;
    message: string;
    status?: number | null;
    details?: string[];
  }) {
    super(params.message);
    this.name = "ApiError";
    this.code = params.code;
    this.status = params.status ?? null;
    this.details = params.details ?? [];
  }
}

function normalizeApiCode(value: string | undefined): ApiErrorCode {
  if (
    value === "validation_error" ||
    value === "not_found" ||
    value === "database_unavailable" ||
    value === "conflict" ||
    value === "unknown_error"
  ) {
    return value;
  }
  return "unknown_error";
}

function isGatewayStatus(status: number): boolean {
  return status === 502 || status === 503 || status === 504;
}

export function isStructuredApiErrorPayload(payload: unknown): payload is ApiErrorPayload {
  if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
    return false;
  }
  return typeof (payload as ApiErrorPayload).error === "string";
}

export function toApiError(status: number, payload: unknown): ApiError {
  if (isGatewayStatus(status) && !isStructuredApiErrorPayload(payload)) {
    return new ApiError({
      code: "network_error",
      status,
      message: "Сервер временно недоступен. Попробуйте обновить страницу.",
    });
  }

  if (!isStructuredApiErrorPayload(payload) && (status === 401 || status === 403 || status === 404)) {
    return new ApiError({
      code: "network_error",
      status,
      message:
        status === 404
          ? "API не найден по этому адресу. Проверьте прокси и префикс /api."
          : "Запрос отклонён сервером. Проверьте переменную CORS_ORIGIN на бэкенде.",
    });
  }

  const maybePayload = payload as ApiErrorPayload;
  const code = normalizeApiCode(maybePayload?.error);
  const details = Array.isArray(maybePayload?.details)
    ? maybePayload.details.filter((item): item is string => typeof item === "string")
    : [];

  return new ApiError({
    code,
    status,
    message: getApiErrorMessage(code),
    details,
  });
}

export function getApiErrorMessage(code: ApiErrorCode): string {
  switch (code) {
    case "validation_error":
      return "Проверьте введённые данные и попробуйте снова.";
    case "not_found":
      return "Запись не найдена.";
    case "database_unavailable":
      return "Сервис временно недоступен. Попробуйте позже.";
    case "conflict":
      return "Запись с таким идентификатором уже существует.";
    case "network_error":
      return "Не удалось связаться с сервером.";
    case "unknown_error":
      return "Сервер не смог обработать запрос. Попробуйте позже или обновите страницу.";
    default:
      return "Произошла неизвестная ошибка.";
  }
}
