import { useEffect } from 'react'
import useRefreshToken from './useRefreshToken';
import useAuth from './useAuth';
import { IsSessionActive, LoadSessionExpireDate, SetSessionExpireDate } from '../user/localStorage';
import { atom, useAtom } from 'jotai';
import useRedirectToLoginPage from './useRedirectToLoginPage';
import { AxiosInstance, InternalAxiosRequestConfig } from 'axios';

const failedRequestsAtom = atom<Array<InternalAxiosRequestConfig>>([]);
const isRefreshTokenFetchingAtom = atom<boolean>(false);

export default function useAxiosWithAuth(client: AxiosInstance) {
    const [ failedRequests, setFailedRequests ] = useAtom(failedRequestsAtom);
    const [ isRefreshTokenFetching, setIsRefreshTokenFetching ] = useAtom(isRefreshTokenFetchingAtom);
    const refresh = useRefreshToken();
	const { accessToken } = useAuth();
	// const [ user ] = useAtom(userAtom);
    // const { toLoginPage } = useRedirectToLoginPage();

    useEffect(() => {
        const requestInterceptor = client.interceptors.request.use(
            async (config: InternalAxiosRequestConfig) => {
                if (accessToken && config.headers.Authorization == undefined){
                    config.headers.Authorization = `Bearer ${accessToken}`;
                }
                // console.log(`Request: ${client.defaults.baseURL}${config.url}; tok: ..${config.headers.Authorization?.toString().slice(-5)}}`);
                return config;
            }, (error) => Promise.reject(error)
        );

        const responseInterceptor = client.interceptors.response.use(
            (response) => response,
            async (error) => {
                // console.log(error); //AxiosError
                const originalRequest = error.config;
                if (error?.response?.status === 401 && !originalRequest?.sent && !originalRequest?._retry){
                    originalRequest._retry = true;
                    try{
                        if (!isRefreshTokenFetching){
                            setIsRefreshTokenFetching(true);
                            // setFailedRequests(failedRequests.concat(originalRequest));
                            const isSessionActive = IsSessionActive();
                            // console.log(`expireDate: [${LoadSessionExpireDate()} > ${new Date()}]: session ${isSessionActive ? 'active' : 'expired'}`);
                            // console.log(`response: ${originalRequest.url}; tok: ..${originalRequest.headers.Authorization?.toString().slice(-5)}, refr:..${refreshToken?.slice(-5)}`);
                            // if (refreshToken == null && accessToken == null){
                            if (accessToken == null && !IsSessionActive()){
                                throw new Error('Session expired. Redirect to login page.');
                            }
                            else{
                                if (isSessionActive){
                                    const newAccessToken = await refresh();
                                    console.log(`newAccessToken: ${newAccessToken}`);
                                    if (newAccessToken == null){
                                        throw new Error('No access token obtained. Redirect to login page.');
                                    }
                                    const requests = failedRequests.map(q => {
                                        q.headers['Authorization'] = `Bearer ${newAccessToken}`;
                                        // q.headers['Authorization'] = `Bearer ${user.accessToken}`;
                                        return client(q)
                                    });
                                    originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
                                    await Promise.all(requests);
                                    setIsRefreshTokenFetching(false);
                                    setFailedRequests([]);
                                    return client(originalRequest);
                                }
                                else{
                                    throw new Error("Refresh token not found. Redirect to login page.");
                                }
                            }
                        }
                        else {
                            failedRequests.push(originalRequest);
                        }
                    }
                    catch (ex){
                        console.log('wtf');
                        console.error(ex);
                        SetSessionExpireDate(null);
                        // toLoginPage();
                        
                        // console.log('drop failed requests queue');
                        setFailedRequests([]);
                        setIsRefreshTokenFetching(false);
                        return Promise.reject(error);
                    }                    
                }
                else{
                    return Promise.reject(error);
                }
            }
        );    
        return () => {
            client.interceptors.request.eject(requestInterceptor);
            client.interceptors.response.eject(responseInterceptor);
        };
    // }, [accessToken, refresh, client, toLoginPage, failedRequests, setFailedRequests, isRefreshTokenFetching, setIsRefreshTokenFetching]);
    }, [accessToken, refresh, client, failedRequests, setFailedRequests, isRefreshTokenFetching, setIsRefreshTokenFetching]);
    // }, []);
}