/* tslint:disable */
/* eslint-disable */
/**
 * EMIL DiscountService
 * The EMIL DiscountService API description
 *
 * The version of the OpenAPI document: 1.0
 * Contact: kontakt@emil.de
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */


import { Configuration } from "./configuration";
import { defaultStorage } from "./common";
// Some imports not used depending on template conditions
// @ts-ignore
import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';


export const BASE_PATH = "https://apiv2.emil.de".replace(/\/+$/, "");
/**
 *
 * @export
 */
export const COLLECTION_FORMATS = {
    csv: ",",
    ssv: " ",
    tsv: "\t",
    pipes: "|",
};

export interface LoginClass {
    accessToken: string;
    permissions: string;
}

export enum Environment {
    Production = 'https://apiv2.emil.de',
    Test = 'https://apiv2-test.emil.de',
    Staging = 'https://apiv2-staging.emil.de',
    Development = 'https://apiv2-dev.emil.de',
    ProductionZurich = 'https://eu-central-2.apiv2.emil.de',
}

let _retry_count = 0
let _retry = null

export function resetRetry() {
    _retry_count = 0
}

/**
 *
 * @export
 * @interface RequestArgs
 */
export interface RequestArgs {
    url: string;
    options: AxiosRequestConfig;
}

interface TokenData {
    accessToken?: string;
    username?: string;
    permissions?: string;
}

const NETWORK_ERROR_MESSAGE = "Network Error";
const TOKEN_DATA = 'APP_TOKEN';

/**
 *
 * @export
 * @class BaseAPI
 */
export class BaseAPI {
    protected configuration: Configuration | undefined;
    private tokenData?: TokenData;

    constructor(configuration?: Configuration,
        protected basePath: string = BASE_PATH, 
        protected axios: AxiosInstance = globalAxios) {
        
        this.loadTokenData();

        if (configuration) {
            this.configuration = configuration;
            this.basePath = configuration.basePath || this.basePath;
            this.configuration.accessToken = this.tokenData.accessToken ? `Bearer ${this.tokenData.accessToken}` : '';
        } else {
            const { accessToken, username } = this.tokenData;

            this.configuration = new Configuration({
                basePath: this.basePath,
                accessToken: accessToken ? `Bearer ${accessToken}` : '',
                username,
            });
        }

        this.attachInterceptor(axios);
    }

    selectEnvironment(env: Environment) {
        this.selectBasePath(env);
    }

    selectBasePath(path: string) {
        this.configuration.basePath = path;
    }

    getPermissions(): Array<string> {
        if (!this.tokenData?.permissions) {
            return [];
        }

        return this.tokenData.permissions.split(',');
    }

    async authorize(username: string, password: string): Promise<void> {
        const options: AxiosRequestConfig = {
            method: 'POST',
            url: `${this.configuration.basePath}/authservice/v1/login`,
            headers: { 'Content-Type': 'application/json' },
            data: {
                username,
                password,
            },
            withCredentials: true,
        };

        const response = await globalAxios.request<LoginClass>(options);

        const { data: { accessToken, permissions } } = response;

        this.configuration.username = username;
        this.configuration.accessToken = `Bearer ${accessToken}`;
        this.tokenData.username = username;
        this.tokenData.accessToken = accessToken;
        this.tokenData.permissions = permissions;

        this.storeTokenData({
            ...this.tokenData
        });
    }

    async refreshTokenInternal(): Promise<LoginClass> {
        const { username } = this.configuration;

        if (!username) {
            throw new Error('Failed to refresh token.');
        }

        const refreshTokenAxios = globalAxios.create()
        refreshTokenAxios.interceptors.response.use(response => {
          const { permissions } = response.data;

          this.tokenData.permissions = permissions;
          this.storeTokenData(this.tokenData);

          return response;
        })

        const options: AxiosRequestConfig = {
            method: 'POST',
            url: `${this.configuration.basePath}/authservice/v1/refresh-token`,
            headers: {
                'Content-Type': 'application/json',
            },
            data: { username: username },
            withCredentials: true,
        };

        const response = await refreshTokenAxios.request<LoginClass>(options);
        return response.data;
    }

    private storeTokenData(tokenData?: TokenData) {
        if (typeof window !== 'undefined') {
            defaultStorage().set<TokenData>(TOKEN_DATA, tokenData);
        }
    }

    public loadTokenData() {
        if (typeof window !== 'undefined') {
            this.tokenData = defaultStorage().get<TokenData>(TOKEN_DATA) || {};
        } else {
            this.tokenData = {};
        }
    }

    public cleanTokenData() {
        this.storeTokenData(null);
    }

    private attachInterceptor(axios: AxiosInstance) {
        axios.interceptors.response.use(
            (res) => {
                return res;
            },
            async (err) => {
                let originalConfig = err.config;
                if (err.response && !(err.response instanceof XMLHttpRequest)) { // sometimes buggy and is of type request
                    // Access Token was expired
                    if ((err.response.status === 401 || err.response.status === 403)
                        && !originalConfig._retry) {
                        originalConfig._retry = true;
                        try {
                            const { accessToken: tokenString, permissions } = await this.refreshTokenInternal();
                            const accessToken = `Bearer ${tokenString}`;
                            this.tokenData.permissions = permissions;

                            delete originalConfig.headers['Authorization']

                            originalConfig.headers['Authorization'] = accessToken;

                            this.configuration.accessToken = accessToken;
                            this.tokenData.accessToken = tokenString;

                            this.storeTokenData(this.tokenData);

                            return axios(originalConfig);
                        } catch (_error) {
                            if (_error.response && _error.response.data) {
                                return Promise.reject(_error.response.data);
                            }
                            return Promise.reject(_error);
                        }
                    }
                } else if (err.message === NETWORK_ERROR_MESSAGE
                    && originalConfig.headers.hasOwnProperty('Authorization')
                    && _retry_count < 4
                ) {
                    _retry_count++;
                    try {
                        const { accessToken: tokenString, permissions } = await this.refreshTokenInternal();
                        const accessToken = `Bearer ${tokenString}`;
                        this.tokenData.permissions = permissions;

                        _retry = true;
                        originalConfig.headers['Authorization'] = accessToken;

                        this.configuration.accessToken = accessToken;
                        this.tokenData.accessToken = tokenString;

                        this.storeTokenData(this.tokenData);

                        return axios.request({
                            ...originalConfig,
                        });
                    } catch (_error) {
                        if (_error.response && _error.response.data) {
                            return Promise.reject(_error.response.data);
                        }
                        return Promise.reject(_error);
                    }
                }
                return Promise.reject(err);
            }
        );
    }
};

/**
 *
 * @export
 * @class RequiredError
 * @extends {Error}
 */
export class RequiredError extends Error {
    override name: "RequiredError" = "RequiredError";
    constructor(public field: string, msg?: string) {
        super(msg);
    }
}

