import {
  API_BASE_PATH,
  AUTH_INFO_KEY,
  CSRF_COOKIE_NAME,
  CSRF_HEADER_NAME,
  ERROR_CODES,
  isAuthRefreshRequest,
  LOCALE_SELECTED_KEY,
} from '@/constants/system'
import { BaseAPIResponse } from '@/models/api/common'
import { readCookie } from '@/util/cookie'
import { refreshTokenAndRetry } from '@/util/token-refresh-queue'
import axios, {
  AxiosError,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios'

const SAFE_METHODS = new Set(['get', 'head', 'options'])

const axiosClient = axios.create({
  baseURL: API_BASE_PATH,
  withCredentials: true,
  headers: {},
  timeout: !isNaN(Number(process.env.NEXT_PUBLIC_TIME_OUT_API))
    ? Number(process.env.NEXT_PUBLIC_TIME_OUT_API)
    : 60000,
})

axiosClient.interceptors.request.use(
  function (config: InternalAxiosRequestConfig) {
    const lang = localStorage.getItem(LOCALE_SELECTED_KEY)
    if (lang) {
      config.headers['Accept-Language'] = lang
    }
    const method = (config.method ?? 'get').toLowerCase()
    if (!SAFE_METHODS.has(method)) {
      const csrfToken = readCookie(CSRF_COOKIE_NAME)
      if (csrfToken) {
        config.headers[CSRF_HEADER_NAME] = csrfToken
      }
    }

    return config
  },
  function (error: unknown) {
    return Promise.reject(
      error instanceof Error ? error : new Error(String(error))
    )
  }
)

axiosClient.interceptors.response.use(
  function (response: AxiosResponse) {
    return response.data
  },
  async function (error: AxiosError<BaseAPIResponse>) {
    const originalRequest = error.config as InternalAxiosRequestConfig & {
      _retry?: boolean
    }

    if (
      error.response?.status === 401 &&
      originalRequest &&
      isAuthRefreshRequest(originalRequest.url)
    ) {
      return Promise.reject(error)
    }

    if (
      error.response?.status === 401 &&
      originalRequest &&
      !originalRequest._retry
    ) {
      const apiErrorCode = error.response?.data?.code
      if (apiErrorCode === ERROR_CODES['PERMISSION_DENIED']) {
        localStorage.removeItem(AUTH_INFO_KEY)
        if (typeof window !== 'undefined') {
          window.location.href = '/?err=PERMISSION_DENIED'
        }
        return Promise.reject(error)
      }

      originalRequest._retry = true

      try {
        const newConfig = await refreshTokenAndRetry(originalRequest)
        return axiosClient(newConfig)
      } catch (refreshError) {
        return Promise.reject(
          refreshError instanceof Error
            ? refreshError
            : new Error(String(refreshError))
        )
      }
    }

    return Promise.reject(error)
  }
)

export default axiosClient
