import type { AxiosError, AxiosInstance } from "axios";
import axios from "axios";
import { createContext, useContext, useMemo } from "react";
import { useToken } from "../token";


export interface AxiosState {
	publicApi: AxiosInstance;
	authorizedApi: AxiosInstance;
}

const initialState: AxiosState = {
	publicApi: {} as AxiosInstance,
	authorizedApi: {} as AxiosInstance
}

const AxiosContext = createContext<AxiosState>(initialState);


export default function AxiosProvider({
	apiUrl,
	children
}: Readonly<{
	apiUrl: string;
	children: React.ReactNode;
}>){
	const { getToken } = useToken();

	const publicApi = useMemo(() => {
		const api = axios.create({
			baseURL: apiUrl,
			withCredentials: true
		});
		return api;
	}, [apiUrl]);
	const authorizedApi = useMemo(() => {
		const api = axios.create({
			baseURL: apiUrl,
			withCredentials: true
		});
		api.interceptors.request.use(async (config) => {
			try{
				const token = await getToken();
				if(token){
					config.headers.Authorization = `Bearer ${token}`;
				}
				return config;
			}
			catch(error){
				return Promise.reject(error as Error);
			}
		});
		api.interceptors.response.use(r => r, async (error: AxiosError) => {
			const original = error.config;
			if(!original){
				return Promise.reject(error);
			}
			if(error.response?.status === 401 && !original._retry){
				original._retry = true;
				try{
					const newToken = await getToken();
					original.headers.Authorization = `Bearer ${newToken}`;
					return api(original);
				}
				catch(refreshError){
					return Promise.reject(refreshError as Error);
				}
			}
		});
		return api;
	}, [apiUrl, getToken]);

	const value = useMemo(() => ({
		publicApi,
		authorizedApi
	}), [authorizedApi, publicApi]);

	return (
		<AxiosContext.Provider value={value}>
			{children}
		</AxiosContext.Provider>
	);
}


// eslint-disable-next-line react-refresh/only-export-components
export function useAxios(){
	const context = useContext(AxiosContext);

	if(!context){
		throw new Error("useAxios must be called inside an AxiosProvider");
	}

	return context;
}
