import { AxiosError } from 'axios'

type UnknownRecord = Record<string, unknown>

const isObject = (value: unknown): value is UnknownRecord =>
  typeof value === 'object' && value !== null

const parseUnknownJson = (raw: string): unknown => {
  try {
    return JSON.parse(raw)
  } catch {
    return null
  }
}

const pickMessageFromData = (data: unknown): string | null => {
  if (!isObject(data)) return null

  const errorValue = data.error
  if (typeof errorValue === 'string') {
    const nested = parseUnknownJson(errorValue)
    if (isObject(nested) && typeof nested.description === 'string') {
      return nested.description
    }
    return errorValue
  }

  if (isObject(errorValue) && typeof errorValue.description === 'string') {
    return errorValue.description
  }

  if (typeof data.message === 'string') {
    return data.message
  }

  return null
}

const parseBlobJson = async (blob: Blob): Promise<unknown> => {
  const text =
    typeof blob.text === 'function'
      ? await blob.text()
      : await new Promise<string>((resolve, reject) => {
          if (typeof FileReader === 'undefined') {
            reject(new Error('FileReader is unavailable'))
            return
          }
          const reader = new FileReader()
          reader.onload = () => resolve(String(reader.result ?? ''))
          reader.onerror = () => reject(reader.error)
          reader.readAsText(blob)
        })
  return parseUnknownJson(text)
}

export const getAxiosErrorMessageAsync = async (
  error: AxiosError<unknown>,
  fallback: string
): Promise<string> => {
  const responseData = error.response?.data
  if (responseData instanceof Blob) {
    const parsed = await parseBlobJson(responseData)
    return pickMessageFromData(parsed) ?? fallback
  }

  return pickMessageFromData(responseData) ?? fallback
}
