import axios, {
    AxiosError,
    AxiosRequestConfig,
    GenericAbortSignal,
} from "axios";
import {toastError} from "../services/toaster";


const {
    REACT_APP_ERROR_HANDLING: ERROR_HANDLING,
    REACT_APP_BFF: BFF,
    NODE_ENV
} = process.env


/*** This how BFF is supposed to return errors */
type AxiosErrorData = {
    message: string; /** User-friendly message (we might show this with a toaster)*/
    error?: {
        message: string  /** Message for debugging*/
        stackTrace: {
            /** error trace from laravel*/
            args: any[]
            class: string
            file: string
            function: string
            line: number
            type: string
        }[]
    }
}

declare module 'axios' {
    export interface AxiosRequestConfig {
        withCache?: boolean;
        cancelable?: boolean
        tag?: string
    }
}

export let abortSignal: GenericAbortSignal | undefined;

export function setAbortSignal(signal: GenericAbortSignal | undefined) {
    abortSignal = signal
}

/**
 * Attaches correct signal before each call
 * Used with combination with useAbortControlled
 * The useAbortControlled hook needs to be used in the components you want the calls to be abortable
 */
export const onRequestFulfilled = (config: AxiosRequestConfig<any>) => {
    return {
        ...config,
        // @ts-ignore
        signal: config.cancelable ? abortSignal : undefined,
    };
}


/**
 * Global Error handling
 * Errors are returned so they can be more specifically handled where they are called
 *
 * Any status codes that falls outside the range of 2xx cause this function to trigger
 */
export const onResponseRejected = (error: AxiosError<AxiosErrorData, any>) => {

    /**
     * HANDLING ERROR IN RESPONSE
     */
    if (error.response) {

        if (ERROR_HANDLING === 'strict') {
            toastError(error.response.status + ": " + error.response.data.message)
        }

        console.error("ERROR IN RESPONSE >>>>>");
        console.error(error.response);

        /**
         * 403 means a user tries to access forbidden content (no authorization)
         */
        if (error.response.status === 403) {
            if (error.response.data.message) {
                toastError(error.response.data.message)
            } else {
                toastError('You do not have the right to access this content')
            }

            /** We choose to refresh the page to force a session prefetch in case the session has expired */
            setTimeout(function () {
                window.location.href = '/';
            }, 1000)
        }

        /**
         * 413 means a user tries to access a resource that requires extra authentication
         */
        if (error.response.status === 413) {
            if (error.response.data.message) {
                toastError(error.response.data.message)
            } else {
                toastError('You need extra authorization to access this content')
            }

            setTimeout(function () {
                window.location.href = '/';
            }, 1000)
        }

        /**
         * 515 returns when the login has been disabled
         * we force reload to get the new Setting call
         */
        if (error.response.status === 515) {
            toastError('Η είσοδος στο D.U.A.Platform προσωρινά δεν είναι διαθέσιμη. Δοκιμάστε ξανά αργότερα.')
            setTimeout(() => {
                document.location.reload()
            }, 3000)
        }

        if (error.response.status === 500) {
            // toastError('Υλοποιούνται εργασίες συντήρησης στον διακομιστή...')
        }
    }

    /**
     * HANDLING ERROR IN REQUEST
     * (WE RECEIVED NO RESPONSE)
     */
    else if (error.request) {

        /**
         * ERROR WAS CANCELLED FROM COMPONENT UNMOUNT / IDLE
         * Must be checked FIRST — canceled errors have no .response,
         * and we don't want to show a toast or log for these.
         */
        if (error.code === 'ERR_CANCELED') {
            return Promise.reject(error);
        }

        if (NODE_ENV === "development") {
            toastError(error.message)
        }

        /**
         * TIMEOUT WAS EXCEEDED
         */
        if (error.code === 'ECONNABORTED') {
            toastError('Η ενέργεια πήρε πολύ χρόνο, προσπαθήστε ξανά')
            return Promise.reject(error);
        }

        /**
         * THIS IS FOR FORBIDDEN TO REDIRECT
         * THIS IS BAD WILL BE FIXED I HOPE
         */
        if (error.code === "ERR_NETWORK") {
            window.location.href = BFF + '/forbidden';
            return
        }

        console.error("ERROR IN REQUEST >>>>>");
        console.error(error.request);
    }

    /**
     * HANDLING OTHER ERRORS
     */
    else {
        console.error("UNKNOWN ERROR IN REQUEST", error.request);
        console.error(error);
    }

    return Promise.reject(error);
}


export const instance = axios.create({
    withCredentials: true,
    baseURL: BFF,
    cancelable: true,
});

instance.interceptors.request.use(
    // @ts-ignore
    onRequestFulfilled
);

instance.interceptors.response.use(
    null,
    onResponseRejected
);

export function sleep(ms = 2000): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
}
