import type { paths } from '~/generated/types.ts'
import type { APIErrorResponse, BackendError, RateLimitStatus } from '~/types'

import createClient from 'openapi-fetch'

import { parseAPIErrors } from '~/utils'
import { i18n } from '~/init/locale'

import moment from 'moment'

import useLogger from '~/composables/useLogger'
import { useRouter } from 'vue-router'
import { useStore } from '~/store'

// Note [WIP] that this module is Work in Progress!
// TODO: Replace all `axios` calls with this client

const { t } = i18n.global
const logger = useLogger()
const store = useStore()
const router = useRouter()

const prefix = '/api/v2/' as const

const client = createClient<paths>({ baseUrl: `${store.state.instance.instanceUrl}${prefix}` })
client.use({
  /*
        TODO: Check if we need these:
        axios.defaults.xsrfCookieName = 'csrftoken'
        axios.defaults.xsrfHeaderName = 'X-CSRFToken'
      */

  async onRequest ({ request, options }) {
    if (store.state.auth.oauth.accessToken) {
      request.headers.set('Authorization', store.getters['auth/header'])
    }
    return request
  },

  async onResponse ({ request, response, options }) {
    return response
  },

  async onError ({ error: unknownError }) {
    const error = unknownError as BackendError
    error.backendErrors = []
    error.isHandled = false

    if (store.state.auth.authenticated && !store.state.auth.oauth.accessToken && error.response?.status === 401) {
      store.commit('auth/authenticated', false)
      logger.warn('Received 401 response from API, redirecting to login form', router.currentRoute.value.fullPath)
      await router.push({ name: 'login', query: { next: router.currentRoute.value.fullPath } })
    }

    switch (error.response?.status) {
      case 404:
        if (error.response?.data === 'Radio doesn\'t have more candidates') {
          error.backendErrors.push(error.response.data)
          break
        }

        error.backendErrors.push('Resource not found')
        error.isHandled = true
        store.commit('ui/addMessage', {
          // @ts-expect-error TS does not know about .data structure
          content: error.response?.data?.detail ?? error.response?.data ?? 'Resource not found',
          class: 'error'
        })
        break

      case 403:
        error.backendErrors.push('Permission denied')
        break

      case 429: {
        let message
        const rateLimitStatus: RateLimitStatus = {
          limit: error.response?.headers['x-ratelimit-limit'],
          description: error.response?.headers['x-ratelimit-scope'],
          remaining: error.response?.headers['x-ratelimit-remaining'],
          duration: error.response?.headers['x-ratelimit-duration'],
          available_seconds: parseInt(error.response?.headers['retry-after'] ?? '60'),
          reset: error.response?.headers['x-ratelimit-reset'],
          reset_seconds: error.response?.headers['x-ratelimit-resetseconds'],
          /* The following threewere not defined. TODO: research correct values */
          id: '',
          available: 100,
          rate: ''
        }

        if (rateLimitStatus.available_seconds) {
          const tryAgain = moment().add(rateLimitStatus.available_seconds, 's').toNow(true)
          message = t('init.axios.rateLimitDelay', { delay: tryAgain })
        } else {
          message = t('init.axios.rateLimitLater')
        }

        error.backendErrors.push(message)
        error.isHandled = true
        store.commit('ui/addMessage', {
          content: message,
          date: new Date(),
          class: 'error'
        })

        logger.error('This client is rate-limited!', rateLimitStatus)
        break
      }

      case 500:
        error.backendErrors.push('A server error occurred')
        break

      default:
        if (error.response?.data as object) {
          const data = error.response?.data as Record<string, unknown>
          if (data?.detail) {
            error.backendErrors.push(data.detail as string)
          } else {
            error.rawPayload = data as APIErrorResponse
            const parsedErrors = parseAPIErrors(data as APIErrorResponse)
            error.backendErrors = [...error.backendErrors, ...parsedErrors]
          }
        }
    }

    if (error.backendErrors.length === 0) {
      error.backendErrors.push('An unknown error occurred, ensure your are connected to the internet and your funkwhale instance is up and running')
    }

    // Do something with response error
    return Promise.reject(error)
  }

  /* TODO: Check if we need to handle refreshAuth = async (failedRequest: AxiosError) */
})

/**
 *
 * Returns `get` and `post` clients for the chosen path (endpoint).
```ts
const { get, post } = useClient('manage/tags');

const result0 = post({ name: 'test' })
const result1 = get({ query: { q: 'test' } })
```
 *
 * @param path The path to create a client for. Check the `paths` type in '~/generated/types.ts' to find all available paths.
 * @param variable
 *
*/
export const useClient = ({

  get: client.GET,

  post: client.POST,

  put: client.PUT,

  patch: client.PATCH,

  delete: client.DELETE

})
