import type {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios'
import axios from 'axios'
import qs from 'qs'
import type { Ref } from 'vue'
import { ElNotification } from 'element-plus'
import { ref } from 'vue'
import { useConfigStoreHook } from '@/stores'

// 创建 axios 实例
let service: AxiosInstance

export interface AxiosResponseData {
  code: number
  content: string
}

export function initService() {
  const configStore = useConfigStoreHook()
  service = axios.create({
    baseURL: configStore.config.apiBaseUrl,
    headers: { 'Content-Type': 'application/json;charset=utf-8' },
    paramsSerializer: (params) => {
      return qs.stringify(params)
    },
  })

  // 请求拦截器
  service.interceptors.request.use(
    (config: InternalAxiosRequestConfig) => {
      return config
    },
    (error: Error) => {
      return Promise.reject(error)
    },
  )

  // 响应拦截器
  service.interceptors.response.use(
    (response: AxiosResponse<AxiosResponseData, unknown>) => {
      // 判断响应状态码是否为2xx
      if (response.status < 200 || response.status >= 300) {
        throw new Error(response.data?.content)
      }

      // 检查配置的响应类型是否为二进制类型（'blob' 或 'arraybuffer'）, 如果是，直接返回响应对象
      if (
        response.config.responseType === 'blob' ||
        response.config.responseType === 'arraybuffer'
      ) {
        return response as unknown as AxiosResponse
      }

      return response.data as unknown as AxiosResponse
    },
    (error: AxiosError<AxiosResponseData | string>) => {
      let message: string = ''
      if (error.response && error.response.status === 500) {
        message = '系统错误'
      }

      // if (error.config?.url === '/irs/data-do' && error.config?.method === 'get') {
      //   return Promise.reject(new Error(message))
      // }

      return Promise.reject(new Error(message))
    },
  )
}

export interface UseAxiosOption {
  // 错误时不弹窗
  hideErrorTip?: boolean
}

// 导出 axios 实例
export async function request<T = unknown, R = AxiosResponse<T>, D = unknown>(
  config: AxiosRequestConfig<D>,
  { hideErrorTip = false } = {} as UseAxiosOption,
): Promise<R> {
  try {
    return await service<T, R, D>(config)
  } catch (error) {
    if (!hideErrorTip) {
      ElNotification({
        showClose: true,
        message: (error as Error)?.message,
        type: 'error',
      })
    }
    return Promise.reject(error)
  }
}

export default request

export interface UseRequestOption<R> extends UseAxiosOption {
  defaultData?: R
}

export interface UseRequestResult<R> {
  data: Ref<R>
  error: Ref<Error | undefined>
  loading: Ref<boolean>
  refresh: () => Promise<void>
}

export function useRequest<T = unknown, R = AxiosResponse<T>, D = unknown>(
  config: AxiosRequestConfig<D>,
  { defaultData = {} as R, ...rest } = {} as UseRequestOption<R>,
) {
  const data = ref<R>(defaultData) as Ref<R>
  const loading = ref<boolean>(false)
  const error = ref<Error>()

  const fetchResource = async (conf = config): Promise<void> => {
    loading.value = true
    try {
      data.value = await request<T, R, D>(conf, rest)
    } catch (e) {
      error.value = e as Error
    } finally {
      loading.value = false
    }
  }

  const promise = new Promise((resolve) => {
    void fetchResource().finally(() => resolve({ data, error, loading, refresh: fetchResource }))
  }) as unknown as UseRequestResult<R> & Promise<UseRequestResult<R>>

  promise.data = data
  promise.error = error
  promise.loading = loading
  promise.refresh = fetchResource

  return promise
}
