import axios from "axios"
import type {Method, AxiosHeaders, AxiosResponse} from "axios"
import type {IApiResponseData, IApiManagerData, JSONObject} from "../model"

export const useAPIManager = ({
  baseUrl,
  headers,
  interceptRequest,
  interceptResponse,
  interceptErrorResponse,
}: IApiManagerData) => {
  const apiInstance = axios.create({
    baseURL: baseUrl,
    headers,
  })

  /* Interception API Request */
  apiInstance.interceptors.request.use(
    config => interceptRequest(config, !window.navigator.onLine),
    error => Promise.reject(error)
  )

  /* Interception API Response */
  apiInstance.interceptors.response.use(
    res => interceptResponse(res),
    err => interceptErrorResponse(err)
  )

  /*
   *@P => Payload
   *@R => Response
   *@E => ErrorData
   */
  const performRequest = async <R, P, E = JSONObject>(
    method: Method,
    url: string,
    data: P | JSONObject = {},
    headers?: JSONObject
  ): Promise<IApiResponseData<R, E>> => {
    try {
      const response = await apiInstance.request({
        method,
        url,
        data,
        headers: headers as AxiosHeaders,
      })
      if (response.status === 200 || response.status === 201) {
        return {data: response.data as R, response}
      } else throw response
    } catch (err) {
      const error = err as JSONObject
      const errorResponse = error["response"] as unknown as AxiosResponse
      const errorData = errorResponse?.["data"] as E
      let errData: IApiResponseData<R, E> = {
        error,
        isNetworkError: !window.navigator.onLine || error["message"] === "Network Error",
      }

      if (errorResponse) {
        errData = {...errData, errorResponse}
      }

      if (errorData) {
        errData = {...errData, errorData}
      }

      return errData
    }
  }

  /*
   *@P => Payload
   *@R => Response
   */
  const getRequest = async <R, E = JSONObject>(url: string, headers?: JSONObject): Promise<IApiResponseData<R, E>> =>
    await performRequest("get", url, null, headers)

  const postRequest = async <R, P, E = JSONObject>(
    url: string,
    data: P,
    headers?: JSONObject
  ): Promise<IApiResponseData<R, E>> => await performRequest("post", url, data, headers)

  const putRequest = async <R, P, E = JSONObject>(
    url: string,
    data: P,
    headers?: JSONObject
  ): Promise<IApiResponseData<R, E>> => await performRequest("put", url, data, headers)

  const deleteRequest = async <R, P, E = JSONObject>(
    url: string,
    data?: P,
    headers?: JSONObject
  ): Promise<IApiResponseData<R, E>> => await performRequest("delete", url, data, headers)

  return {
    apiInstance,
    postRequest,
    getRequest,
    putRequest,
    deleteRequest,
  }
}
