import {
  buildBearerHeaders,
  notifyAuthSessionExpired,
  readAuthAccessToken
} from '../utils/authSessionStorage.js'

const API_BASE_STORAGE_KEY = 'x-financial-api-base-url'

function isHeaderValueSafe(value) {
  const normalized = String(value || '').trim()
  if (!normalized) {
    return false
  }

  for (let index = 0; index < normalized.length; index += 1) {
    const codePoint = normalized.charCodeAt(index)
    if (codePoint > 255 || codePoint === 10 || codePoint === 13 || codePoint === 0) {
      return false
    }
  }

  return true
}

export function pickSafeHeaderValue(value, fallback = '') {
  const normalized = String(value || '').trim()
  if (isHeaderValueSafe(normalized)) {
    return normalized
  }

  const fallbackValue = String(fallback || '').trim()
  if (isHeaderValueSafe(fallbackValue)) {
    return fallbackValue
  }

  return ''
}

function normalizeApiBaseUrl(value) {
  return String(value || '/api/v1').replace(/\/$/, '')
}

function isLoopbackHost(hostname) {
  const normalized = String(hostname || '').trim().toLowerCase()
  return normalized === '127.0.0.1' || normalized === 'localhost' || normalized === '0.0.0.0' || normalized === '::1'
}

function resolveBrowserReachableApiBaseUrl(value) {
  const normalized = normalizeApiBaseUrl(value)

  if (typeof window === 'undefined') {
    return normalized
  }

  try {
    const apiUrl = new URL(normalized)
    const browserHost = window.location.hostname

    if (isLoopbackHost(apiUrl.hostname) && browserHost && !isLoopbackHost(browserHost)) {
      apiUrl.hostname = browserHost
      return normalizeApiBaseUrl(apiUrl.toString())
    }
  } catch {
    return normalized
  }

  return normalized
}

function readStoredApiBaseUrl() {
  if (typeof window === 'undefined') {
    return ''
  }

  return resolveBrowserReachableApiBaseUrl(window.localStorage.getItem(API_BASE_STORAGE_KEY) || '')
}

let runtimeApiBaseUrl = normalizeApiBaseUrl('/api/v1')

if (typeof window !== 'undefined') {
  window.localStorage.removeItem(API_BASE_STORAGE_KEY)
}

export function setRuntimeApiBaseUrl(value) {
  runtimeApiBaseUrl = resolveBrowserReachableApiBaseUrl(value)

  if (typeof window !== 'undefined') {
    window.localStorage.setItem(API_BASE_STORAGE_KEY, runtimeApiBaseUrl)
  }
}

export function getRuntimeApiBaseUrl() {
  return runtimeApiBaseUrl
}

function buildUrl(path) {
  if (!path.startsWith('/')) {
    return `${runtimeApiBaseUrl}/${path}`
  }

  return `${runtimeApiBaseUrl}${path}`
}

function formatValidationLocation(loc) {
  if (!Array.isArray(loc)) {
    return ''
  }

  return loc
    .filter((item) => item !== 'body')
    .map((item) => String(item || '').trim())
    .filter(Boolean)
    .join('.')
}

function resolveErrorMessage(payload, fallback = '接口请求失败，请稍后重试。') {
  const detail = payload?.detail

  if (typeof detail === 'string' && detail.trim()) {
    return detail.trim()
  }

  if (Array.isArray(detail) && detail.length) {
    const messages = detail
      .map((item) => {
        if (typeof item === 'string' && item.trim()) {
          return item.trim()
        }

        if (!item || typeof item !== 'object') {
          return ''
        }

        const message = String(item.msg || item.message || '').trim()
        const location = formatValidationLocation(item.loc)

        if (location && message) {
          return `${location}: ${message}`
        }

        return message
      })
      .filter(Boolean)

    if (messages.length) {
      return messages.join('；')
    }
  }

  if (detail && typeof detail === 'object') {
    const message = String(detail.message || detail.msg || '').trim()
    if (message) {
      return message
    }
  }

  if (typeof payload?.message === 'string' && payload.message.trim()) {
    return payload.message.trim()
  }

  return fallback
}

function sanitizeHeaders(headers) {
  const nextHeaders = {}

  Object.entries(headers || {}).forEach(([key, value]) => {
    if (typeof value === 'undefined' || value === null) {
      return
    }

    const normalizedValue = String(value).trim()
    if (!normalizedValue) {
      return
    }

    if (!isHeaderValueSafe(normalizedValue)) {
      return
    }

    nextHeaders[key] = normalizedValue
  })

  return nextHeaders
}

function buildApiResponseError(response, payload, { auth, handleUnauthorized }) {
  const error = new Error(resolveErrorMessage(payload))
  error.status = response.status
  error.payload = payload
  error.detail = payload?.detail
  if (payload?.detail && typeof payload.detail === 'object') {
    error.code = String(payload.detail.code || '').trim()
    error.review = payload.detail.review || null
  }
  if (response.status === 401 && auth && handleUnauthorized && readAuthAccessToken()) {
    error.code = 'AUTH_SESSION_EXPIRED'
    notifyAuthSessionExpired()
  }
  return error
}

export async function apiRequest(path, options = {}) {
  const {
    auth = true,
    handleUnauthorized = true,
    contentType = 'application/json',
    responseType = 'json',
    headers: customHeaders,
    timeoutMs = 0,
    timeoutMessage = '',
    ...fetchOptions
  } = options

  const headers = sanitizeHeaders(customHeaders || {})
  if (auth) {
    Object.keys(headers).forEach((key) => {
      if (key.toLowerCase() === 'authorization') {
        delete headers[key]
      }
    })
    Object.assign(headers, buildBearerHeaders())
  }

  if (contentType !== null && typeof headers['Content-Type'] === 'undefined') {
    headers['Content-Type'] = contentType
  }

  let response
  let timeoutId = 0
  let requestOptions = {
    ...fetchOptions,
    headers
  }

  if (!fetchOptions.signal && Number(timeoutMs) > 0 && typeof AbortController !== 'undefined') {
    const controller = new AbortController()
    timeoutId = globalThis.setTimeout(() => controller.abort(), Number(timeoutMs))
    requestOptions = {
      ...requestOptions,
      signal: controller.signal
    }
  }

  try {
    response = await fetch(buildUrl(path), requestOptions)
  } catch (error) {
    if (timeoutId) {
      globalThis.clearTimeout(timeoutId)
    }
    if (error?.name === 'AbortError') {
      const timeoutError = new Error(String(timeoutMessage || '').trim() || '接口请求超时，请稍后重试。')
      timeoutError.code = 'REQUEST_TIMEOUT'
      throw timeoutError
    }
    if (String(error?.message || '').includes('ByteString')) {
      throw new Error('当前登录用户信息包含浏览器不支持的请求头字符，请重新登录后重试。')
    }

    throw new Error('无法连接 FastAPI 后端服务，请确认后端已启动且浏览器可访问后端端口。')
  }

  if (timeoutId) {
    globalThis.clearTimeout(timeoutId)
  }

  if (responseType === 'blob') {
    if (!response.ok) {
      let payload = null

      try {
        payload = await response.json()
      } catch {
        payload = null
      }

      throw buildApiResponseError(response, payload, { auth, handleUnauthorized })
    }

    return response.blob()
  }

  let payload = null
  try {
    payload = await response.json()
  } catch {
    payload = null
  }

  if (!response.ok) {
    throw buildApiResponseError(response, payload, { auth, handleUnauthorized })
  }

  return payload
}
