import axios, {AxiosError, AxiosResponse} from 'axios'
import Game, {Data, GameList} from '../models/game'
import {toast} from 'react-toastify'
import globalRouter from "./globalRouter";
import User, {IForgotPassword, IResetPassword, UserAPI, UserFormValues, UserUpdateValues} from '../models/user'
import {userToken} from '../stateManagement/userState'
import {RecursivePartial} from "../@types/game";

const sleep = (delay: number) => {
    return new Promise((resolve) => {
        setTimeout(resolve, delay)
    })
}
axios.defaults.baseURL = import.meta.env.VITE_API_URL

axios.interceptors.request.use((config) => {
    const token = userToken.get()
    if (token) config.headers.Authorization = `Bearer ${token}`
    return config
})

axios.interceptors.response.use(
    async (response) => {
        await sleep(0)
        return response
    },
    (error: AxiosError) => {
        const {data, status, config} = error.response!
        switch (status) {
            case 400:
                // @ts-ignore
                if (typeof data.error === 'string') toast.error(data.error)
                if (config.method === 'get' && globalRouter.navigate) globalRouter.navigate('/not-found')
                // @ts-ignore
                if (data.errors) {
                    const modalStateErrors = []
                    // @ts-ignore
                    for (const key in data.errors) {
                        // @ts-ignore
                        if (data.errors[key]) modalStateErrors.push(data.errors[key])
                    }
                    throw modalStateErrors.flat()
                }
                break
            case 401:
                toast.error('Unauthorized')
                if (globalRouter.navigate) globalRouter.navigate('/')
                break
            case 404:
                if (globalRouter.navigate) globalRouter.navigate('/not-found')
                break
            case 429:
                if (globalRouter.navigate) globalRouter.navigate('/not-found')
                break
            case 500:
                toast.error('Server Error')
                break
        }
        return Promise.reject(error)
    }
)

const responseBody = <T>(response: AxiosResponse<T>) => response.data

interface Tags {
    success: boolean,
    data: {
        genre: string[]
        series: string[]
        store: string[]
        developer: string[]
        publisher: string[]
    }
}

const requests = {
    get: <T>(url: string) => axios.get<T>(url).then(responseBody),
    post: <T>(url: string, body: {}) =>
        axios.post<T>(url, body).then(responseBody),
    put: <T>(url: string, body: {}) => axios.put<T>(url, body).then(responseBody),
    delete: <T>(url: string) => axios.delete<T>(url).then(responseBody),
}

const Games = {
    list: (filter: string) => requests.get<GameList>(`/games?${filter}`),
    details: (id: string) => requests.get<Game>(`/games/${id}`),
    create: (game: Data) => axios.post<void>('/games', game),
    update: (id: string, game: Data) => axios.put<void>(`/games/${id}`, game),
    playStatus: (id: string, playStatus: RecursivePartial<Data>) => axios.put<void>(`/games/${id}`, playStatus),
    rating: (id: string, rating: RecursivePartial<Data>) => axios.put<void>(`/games/${id}`, rating),
    delete: (id: string) => axios.delete<void>(`/games/${id}`),
}

const GamesAdmin = {
    list: (filter: string) => requests.get<GameList>(`/admin/games?${filter}`),
    details: (id: string) => requests.get<Game>(`/admin/games/${id}`),
    create: (game: Data) => axios.post<void>('/admin/games', game),
    update: (id: string, game: Data) => axios.put<void>(`/admin/games/${id}`, game),
    delete: (id: string) => axios.delete<void>(`/admin/games/${id}`),
}

const RetrieveTags = {
    tags: () => requests.get<Tags>('/tags'),
    genres: () => requests.get<string[]>('/tags/genres'),
    series: () => requests.get<string[]>('/tags/series'),
    store: () => requests.get<string[]>('/tags/store'),
    developer: () => requests.get<string[]>('/tags/developer'),
    publisher: () => requests.get<string[]>('/tags/publisher'),
}

const Account = {
    current: () => requests.get<UserAPI>('/auth/me'),
    login: (user: UserFormValues) => requests.post<User>('/auth/login', user),
    register: (user: UserFormValues) => requests.post<User>('/auth/register', user),
    forgot: (email: IForgotPassword) => requests.post('/auth/forgotpassword', email),
    reset: (token: string, password: IResetPassword) => requests.put(`/auth/resetpassword/${token}`, password),
    update: (user: UserUpdateValues) => requests.put('/auth/updatedetails', user)
}

const agent = {
    Games,
    GamesAdmin,
    RetrieveTags,
    Account,
}

export default agent
