// eslint-disable-file no-param-reassign
// eslint-disable-next-line max-classes-per-file
import axios, { AxiosRequestConfig } from 'axios';
import { Observable, Subject, Subscriber } from 'rxjs';

const Reset = '\x1b[0m';
const Bright = '\x1b[1m';
const Dim = '\x1b[2m';
const Underscore = '\x1b[4m';
const Blink = '\x1b[5m';
const Reverse = '\x1b[7m';
const Hidden = '\x1b[8m';

const FgBlack = '\x1b[30m';
const FgRed = '\x1b[31m';
const FgGreen = '\x1b[32m';
const FgYellow = '\x1b[33m';
const FgBlue = '\x1b[34m';
const FgMagenta = '\x1b[35m';
const FgCyan = '\x1b[36m';
const FgWhite = '\x1b[37m';

const BgBlack = '\x1b[40m';
const BgRed = '\x1b[41m';
const BgGreen = '\x1b[42m';
const BgYellow = '\x1b[43m';
const BgBlue = '\x1b[44m';
const BgMagenta = '\x1b[45m';
const BgCyan = '\x1b[46m';
const BgWhite = '\x1b[47m';

class AxiosSubscriber extends Subscriber<any> {
    source = null;

    completed = false;

    constructor(observer, axiosRequestConfig: AxiosRequestConfig, httpService: HttpService) {
        super(observer);

        this.source = axios.CancelToken.source();

        /**
         * Se Mock Erro estiver ativo
         */
        if (httpService.mockError) {
            const errorMessage = {
                success: false,
                detail: {
                    description: 'Erro ao conectar com servidor, verifique sua internet',
                },
            };

            observer.error(errorMessage);
            observer.unsubscribe();
            return;
        }

        // eslint-disable-next-line no-param-reassign
        axiosRequestConfig.cancelToken = this.source.token;

        console.log(`${`${BgBlue} ${axiosRequestConfig.method.toUpperCase()} `}\x1b[0m`, axiosRequestConfig.url);

        axios
            .request(axiosRequestConfig)
            .then((response) => {
                if (typeof response.data === 'string') {
                    observer.error({
                        success: false,
                        detail: {
                            description: 'Erro ao realizar ação, tente novamente mais tarde',
                        },
                    });
                } else {
                    console.log(`${`${BgGreen} Success `}\x1b[0m`, response.data);
                    observer.next(response.data);
                    this.completed = true;
                    observer.complete();
                    observer.unsubscribe();
                }
            })
            .catch((error) => {
                this.completed = true;

                httpService.listenersErrors.forEach((handle) => handle(error));

                let errorMessage = null;

                try {
                    errorMessage = error.response.data;
                } catch (e) {
                    errorMessage = {
                        success: false,
                        detail: {
                            description: 'Erro ao conectar com servidor, verifique sua internet',
                        },
                    };
                }

                console.log(`${`${BgRed} Error `}\x1b[0m`, errorMessage);

                observer.error(errorMessage);
                observer.unsubscribe();
            });
    }

    unsubscribe(): any {
        if (!this.completed && this.source) {
            this.source.cancel();
        }
    }
}

class HttpService {
    defaults: any = {
        headers: {} as any,
    };

    listenersErrors: ((error: any) => any)[] = [];

    /**
     * Atenção, está variável só deve ser usada em desenvolvimento
     * para simular erros na request de todas as requisições
     */
    mockError = false;

    get(url: string, headers?: any): Observable<any> {
        return new Observable((subscriber) => {
            return new AxiosSubscriber(
                subscriber,
                {
                    method: 'get',
                    url,
                    headers: { ...this.defaults.headers, ...headers },
                },
                this,
            );
        });
    }

    post(url: string, body: any = {}, headers?: any): Observable<any> {
        return new Observable((subscriber) => {
            return new AxiosSubscriber(
                subscriber,
                {
                    method: 'post',
                    data: body,
                    url,
                    headers: { ...this.defaults.headers, ...headers },
                },
                this,
            );
        });
    }

    delete(url: string, body: any = {}, headers?: any): Observable<any> {
        return new Observable((subscriber) => {
            return new AxiosSubscriber(
                subscriber,
                {
                    method: 'delete',
                    data: body,
                    url,
                    headers: { ...this.defaults.headers, ...headers },
                },
                this,
            );
        });
    }
}

export default new HttpService();
