import type { ApiError } from "@/client"

export interface TracecatApiError<T = unknown> extends ApiError {
  readonly body: {
    detail?: T
    message?: string | null
  }
}

export function retryHandler(failureCount: number, error: ApiError) {
  // Check for 4XX errors and terminate
  if (Math.floor(error.status / 100) === 4) {
    console.debug("Got 4XX error, terminating early")
    return false
  }
  // Retry for all other errors up to 3 times
  return failureCount < 3
}

/**
 * Type for request validation errors
 * Returned with 422 status code
 */
export interface RequestValidationError {
  loc: string[]
  ctx: {
    [key: string]: unknown
  }
  msg: string
  type: string
}

export function isRequestValidationError(
  obj: unknown
): obj is RequestValidationError {
  return typeof obj === "object" && obj !== null && "loc" in obj && "msg" in obj
}

export function isRequestValidationErrorArray(
  obj: unknown
): obj is RequestValidationError[] {
  return Array.isArray(obj) && obj.every((o) => isRequestValidationError(o))
}

export function getApiErrorDetail(error: unknown): string | null {
  if (!(error instanceof Error)) {
    return null
  }

  const maybeApiError = error as TracecatApiError<unknown>
  const detail = maybeApiError.body?.detail
  if (typeof detail === "string") {
    return detail
  }
  if (detail != null) {
    try {
      return JSON.stringify(detail)
    } catch {
      return error.message
    }
  }
  const message = maybeApiError.body?.message
  if (typeof message === "string" && message.length > 0) {
    return message
  }
  return error.message
}

/**
 * Extract a structured `code` field from an API error's detail payload, when
 * the backend returns `{ "code": "...", ... }` for machine-readable handling.
 */
export function getApiErrorCode(error: unknown): string | null {
  if (!(error instanceof Error)) {
    return null
  }
  const detail = (error as TracecatApiError<unknown>).body?.detail
  if (
    typeof detail === "object" &&
    detail !== null &&
    "code" in detail &&
    typeof (detail as { code: unknown }).code === "string"
  ) {
    return (detail as { code: string }).code
  }
  return null
}
