import axios, { AxiosRequestConfig, AxiosError } from "axios";
import { API_URL, reportAxiosError } from "../utils/RequestUtils";
import { authStore } from "../stores/AuthStore";
import * as SecureStore from 'expo-secure-store'

/**
 * Hook that exposes common authentication logic.
 * @category Hooks
 * @namespace useAuth
 */
export const useAuth = () => {
    /**
     * Logs out the user
     * @memberOf useAuth
     */
    const logout = async () => {
        try {
            const tokenParams = new URLSearchParams();
            tokenParams.append('client_id', 'atlas.mobile');
            tokenParams.append('token', authStore.accessToken as string);
            
            const response = await axios.post(API_URL + `/o/revoke-token/`, tokenParams, {
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
            });  

            await SecureStore.deleteItemAsync('access');
            authStore.setAccessTokenAsync(null);
        } catch (error) {
            reportAxiosError("Something went wrong when retrieving access token", error);
        } 
    }

    const getNotificationTokenFromServer = async () => {
        const config: AxiosRequestConfig = {
            method: 'GET',
            url: API_URL + `/api/notif-token/${authStore.userId}/`,
            headers: { "Authorization": "Bearer " + authStore.accessToken, }
        }
        
        try {
            const response = await axios(config);   
            return response.data;
        } catch (error) {
            if (error.response.status == 401) {
                try {
                    await refreshAccessToken()    
                    const response = await axios({...config, headers: { "Authorization": "Bearer " + authStore.accessToken }});   

                    return response.data;
                } catch (error) {
                    // refreshAccessToken will report errors
                }
            }

            reportAxiosError('Something went wrong when retrieving landmarks', error)
            throw new Error;
        }
    }

    const ensureNotificationTokenExistsOnServer = async (notificationToken: string, retries: number) => {
        if (retries > 0) {
            const config: AxiosRequestConfig = {
                method: 'POST',
                data: {token: notificationToken},
                url: API_URL + `/api/notif-token/${authStore.userId}/`,
                headers: { "Authorization": "Bearer " + authStore.accessToken, }
            } 
            try {
                const response = await axios(config);   
                return response.data;
            } catch (error) {
                if (error.response.status == 401) {
                    try {
                        await refreshAccessToken()    
                        const response = await axios({...config, headers: { "Authorization": "Bearer " + authStore.accessToken }});   

                        return response.data;
                    } catch (error) {
                        // refreshAccessToken will report errors
                    }
                }
                reportAxiosError('Something went wrong when retrieving landmarks', error)
            }
    
            ensureNotificationTokenExistsOnServer(notificationToken, retries - 1)   
        }
        else {
            throw new Error;
        }
    }

    /**
     * If there is a refresh token available, attempts to use it to obtain a new, valid access token.
     * Used in {@link Atlas}
     * @memberOf useAuth
     */
    const refreshAccessToken = async () => {
        if (authStore.refreshToken) {
            try {
                const tokenData = new URLSearchParams();
                tokenData.append('grant_type', 'refresh_token');
                tokenData.append('refresh_token', authStore.refreshToken);
                tokenData.append('client_id', 'atlas.mobile');
                console.log('Attempting to refresh token...')
                const { data: refreshResponseData } = await axios.post(API_URL + "/o/token/", tokenData, {
                    headers: { 'Content-Type': "application/x-www-form-urlencoded" }
                });   
                
                await authStore.setRefreshTokenAsync(refreshResponseData.refresh_token);
                await authStore.setAccessTokenAsync(refreshResponseData.access_token);

                console.info('Successfully refreshed access token, re-attempting initial request...')
            }
            catch (error) {
                reportAxiosError("Error when trying to refresh access token", error);            
            }
        }
    }

    return { refreshAccessToken, logout, getNotificationTokenFromServer, ensureNotificationTokenExistsOnServer }
}