import type { Api } from 'nocodb-sdk'
const DbNotFoundMsg = 'Database config not found'

const TIMEOUT_RETRY_COUNT = 1

export function addAxiosInterceptors(api: Api<any>, skipSocket = false) {
  const state = useGlobal()
  const router = useRouter()
  const route = router.currentRoute
  const optimisedQuery = useState('optimisedQuery', () => true)
  const { $ncSocket } = useNuxtApp()

  const axiosInstance = api.instance

  axiosInstance.interceptors.request.use((config) => {
    config.headers['xc-gui'] = 'true'
    config.headers['xc-socket-id'] = skipSocket ? null : $ncSocket.id() || null

    if (state.token.value && !config.headers['xc-short-token']) {
      config.headers['xc-auth'] = state.token.value
    }

    if (!config.url?.endsWith('/user/me') && !config.url?.endsWith('/admin/roles')) {
      if (route.value && route.value.params && route.value.params.typeOrId === 'base') {
        config.headers['xc-shared-base-id'] = route.value.params.baseId
        delete config.headers['xc-auth']
      } else if (route.value && route.value.params && route.value.params.typeOrId === 'ERD') {
        config.headers['xc-shared-erd-id'] = route.value.params.erdUuid
        delete config.headers['xc-auth']
      }
    }

    if (!optimisedQuery.value) {
      config.params = { ...(config.params ?? {}), opt: 'false' }
    }

    return config
  })

  axiosInstance.interceptors.response.use(
    (response) => response,
    async (error) => {
      const isSharedPage =
        route.value?.params?.typeOrId === 'base' || route.value?.params?.typeOrId === 'ERD' || route.value.meta.public

      if (error.code === 'ERR_CANCELED') return Promise.reject(error)

      if (error.response?.data?.msg === DbNotFoundMsg) {
        return router.replace('/base/0')
      }

      if (!error.response || error.response.status !== 401) {
        return Promise.reject(error)
      }

      if (error.config.url === '/auth/token/refresh') {
        await state.signOut({
          redirectToSignin: !route.value.meta.public,
          skipApiCall: true,
        })
        return Promise.reject(error)
      }

      let retry = 0
      do {
        try {
          const token = await state.refreshToken({
            axiosInstance,
            skipLogout: true,
          })

          if (!token) {
            await state.signOut({
              redirectToSignin: !isSharedPage,
              skipApiCall: true,
            })
            return Promise.reject(error)
          }

          const config = error.config
          config.headers['xc-auth'] = token

          const response = await axiosInstance.request(config)
          return response
        } catch (refreshTokenError) {
          if ((refreshTokenError as any)?.code === 'ERR_CANCELED') {
            return Promise.reject(refreshTokenError)
          }

          // if shared execution error, don't sign out
          if (!(refreshTokenError instanceof SharedExecutionError)) {
            await state.signOut({
              redirectToSignin: !isSharedPage,
              skipApiCall: true,
            })
            return Promise.reject(error)
          }

          if (retry >= TIMEOUT_RETRY_COUNT) return Promise.reject(error)
        }
      } while (retry++ < TIMEOUT_RETRY_COUNT)
    },
  )

  return api
}
