import Taro from '@tarojs/taro'
import { getToken, redirectToLogin } from './auth'
import type { ResponseData } from '@/api/types'

type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'

interface RequestOptions {
  method: RequestMethod
  url: string
  data?: unknown
  params?: object
  showLoading?: boolean
}

interface UploadOptions {
  url: string
  filePath: string
  fileName?: string
  name?: string
  formData?: Record<string, string | number | boolean>
  showLoading?: boolean
}

const baseURL = process.env.TARO_APP_API_BASE || 'http://localhost:8080'

export async function request<T>(options: RequestOptions) {
  if (options.showLoading !== false) {
    Taro.showLoading({ title: '加载中', mask: true })
  }
  try {
    return await sendRequest<T>(options)
  } finally {
    if (options.showLoading !== false) {
      Taro.hideLoading()
    }
  }
}

async function sendRequest<T>(options: RequestOptions) {
  const response = await Taro.request<ResponseData<T>>({
    url: buildUrl(options.url, options.params),
    method: options.method,
    data: options.data,
    header: buildHeaders()
  })
  return handleResponse(response.data)
}

export async function uploadFile<T>(options: UploadOptions) {
  if (options.showLoading !== false) {
    Taro.showLoading({ title: '上传中', mask: true })
  }
  try {
    return await sendUpload<T>(options)
  } finally {
    if (options.showLoading !== false) {
      Taro.hideLoading()
    }
  }
}

async function sendUpload<T>(options: UploadOptions) {
  const response = await Taro.uploadFile({
    url: buildUrl(options.url),
    filePath: options.filePath,
    name: options.name || 'file',
    fileName: options.fileName,
    formData: options.formData,
    header: buildUploadHeaders()
  })
  validateUploadStatus(response.statusCode, response.data)
  return handleResponse(parseUploadData<T>(response.data))
}

function validateUploadStatus(statusCode: number, data: string) {
  if (statusCode >= 200 && statusCode < 300) return
  const message = getUploadErrorMessage(data) || `上传失败(${statusCode})`
  throw new Error(message)
}

function buildUrl(url: string, params?: object) {
  const target = `${baseURL}${url}`
  const query = new URLSearchParams()
  Object.entries(params || {}).forEach(([key, value]) => appendQuery(query, key, value))
  const queryString = query.toString()
  return queryString ? `${target}?${queryString}` : target
}

function appendQuery(query: URLSearchParams, key: string, value: unknown) {
  if (value === undefined || value === null || value === '') return
  query.append(key, String(value))
}

function buildHeaders() {
  return { ...buildUploadHeaders(), 'Content-Type': 'application/json' }
}

function buildUploadHeaders() {
  const token = getToken()
  const headers: Record<string, string> = {
    Accept: 'application/json, text/plain, */*'
  }
  if (token) {
    headers.Authorization = `Bearer ${token}`
  }
  return headers
}

function parseUploadData<T>(data: string | object) {
  if (typeof data !== 'string') return data as ResponseData<T>
  try {
    return JSON.parse(data) as ResponseData<T>
  } catch {
    throw new Error(getUploadErrorMessage(data) || '服务器返回数据结构有误')
  }
}

function getUploadErrorMessage(data: string) {
  try {
    return (JSON.parse(data) as { msg?: string }).msg || ''
  } catch {
    return data.slice(0, 60)
  }
}

function handleResponse<T>(data: ResponseData<T>) {
  if (!data || data.code === undefined) {
    throw new Error('服务器返回数据结构有误')
  }
  if (data.code === 106) {
    redirectToLogin()
    throw new Error(data.msg || '登录状态已过期')
  }
  if (data.code !== 0) {
    Taro.showToast({ title: data.msg || '请求失败', icon: 'none' })
    throw new Error(data.msg || '请求失败')
  }
  return data
}

