import axios, { AxiosError, AxiosRequestHeaders } from 'axios';
import SessionManager                             from './SessionManager';
import FormData                                   from 'form-data';
import { StatusCodes }                            from 'http-status-codes';
import ClientsSharedConfig                        from '../sharedByClients/config/ClientsSharedConfig';
import { version }                                from '../config/Version';
import DojoBackendResponse                        from '../shared/types/Dojo/DojoBackendResponse';
import DojoStatusCode                             from '../shared/types/Dojo/DojoStatusCode';
import boxen                                      from 'boxen';
import Config                                     from '../config/Config';
import SharedConfig                               from '../shared/config/SharedConfig';
import { stateConfigFile }                        from '../config/ConfigFiles';


class HttpManager {
    public handleAuthorizationCommandErrors: boolean = true;

    registerAxiosInterceptor() {
        this.registerRequestInterceptor();
        this.registerResponseInterceptor();
    }

    private registerRequestInterceptor() {
        axios.interceptors.request.use((config) => {

            if ( config.data instanceof FormData ) {
                config.headers = { ...config.headers, ...(config.data as FormData).getHeaders() } as AxiosRequestHeaders;
            }

            if ( config.url && (config.url.indexOf(ClientsSharedConfig.apiURL) !== -1) ) {
                config.headers['Accept-Encoding'] = 'gzip';

                if ( config.data && Object.keys(config.data).length > 0 ) {
                    config.headers['Content-Type'] = 'multipart/form-data';
                }

                if ( SessionManager.isLogged ) {
                    config.headers.Authorization = `Bearer ${ SessionManager.apiToken }`;
                }

                config.headers['client'] = 'DojoCLI';
                config.headers['client-version'] = version;
            }

            if ( SessionManager.gitlabCredentials.accessToken && config.url && config.url.indexOf(SharedConfig.gitlab.apiURL) !== -1 ) {
                config.headers.Authorization = `Bearer ${ SessionManager.gitlabCredentials.accessToken }`;
            }

            return config;
        });
    }

    private requestError(message: string) {
        console.log(boxen(message, {
            title         : 'Request error',
            titleAlignment: 'center',
            borderColor   : 'red',
            borderStyle   : 'bold',
            margin        : 1,
            padding       : 1,
            textAlignment : 'left'
        }));
        process.exit(1);
    }

    private registerResponseInterceptor() {
        axios.interceptors.response.use((response) => {
            if ( response.data && response.data.sessionToken ) {
                SessionManager.apiToken = response.data.sessionToken;
            }

            if ( response.headers['dojocli-latest-version'] ) {
                const latestDojoCliVersion = response.headers['dojocli-latest-version'];
                const storedLatestDojoCliVersion = stateConfigFile.getParam('latestDojoCliVersion') as string | null || '0.0.0';

                if ( latestDojoCliVersion !== storedLatestDojoCliVersion ) {
                    stateConfigFile.setParam('latestDojoCliVersion', latestDojoCliVersion);
                    stateConfigFile.setParam('latestDojoCliVersionNotification', 0);
                }
            }

            return response;
        }, async (error) => {
            if ( error.response ) {
                const originalConfig = error.config;

                const isFromApi = error.response.config.url && error.response.config.url.indexOf(ClientsSharedConfig.apiURL) !== -1;
                const isFromGitlab = error.response.config.url && error.response.config.url.indexOf(SharedConfig.gitlab.URL) !== -1;

                // Try to refresh the Gitlab tokens if the request have failed with a 401 error
                if ( error.response.status === StatusCodes.UNAUTHORIZED && isFromGitlab && !originalConfig._retry ) {
                    originalConfig._retry = true;

                    try {
                        await SessionManager.refreshTokens();

                        return axios(originalConfig);
                    } catch ( error: unknown ) {
                        if ( error instanceof AxiosError ) {
                            if ( error.response && error.response.data ) {
                                return Promise.reject(error.response.data);
                            }
                        }

                        return Promise.reject(error);
                    }
                }

                if ( error.response.status === StatusCodes.METHOD_NOT_ALLOWED && isFromApi && error.response.data ) {
                    const data: DojoBackendResponse<void> = error.response.data;

                    switch ( data.code ) {
                        case DojoStatusCode.CLIENT_NOT_SUPPORTED:
                            this.requestError('Client not recognized by the server. Please contact the administrator.');
                            break;
                        case DojoStatusCode.CLIENT_VERSION_NOT_SUPPORTED:
                            this.requestError(`CLI version not anymore supported by the server. Please update the CLI.\nYou can download the latest stable version on this page:\n${ Config.gitlab.cliReleasePage }`);
                            break;
                        default:
                            break;
                    }
                }

                if ( this.handleAuthorizationCommandErrors ) {
                    if ( isFromApi ) {
                        switch ( error.response.status ) {
                            case StatusCodes.UNAUTHORIZED:
                                this.requestError('Session expired or does not exist. Please login again.');
                                break;
                            case StatusCodes.FORBIDDEN:
                                this.requestError('Forbidden access.');
                                break;
                        }
                    }
                } else {
                    this.handleAuthorizationCommandErrors = true;
                }
            } else {
                this.requestError('Error connecting to the server. Please check your internet connection. If the problem persists, please contact the administrator.');
            }

            return Promise.reject(error);
        });
    }
}


export default new HttpManager();