/* Copyright CloudLinux Zug GmbH 2010-2017 All Rights Reserved
 *
 * Licensed under CLOUD LINUX LICENSE AGREEMENT
 * http://cloudlinux.com/docs/LICENSE.TXT
 */

import {Injectable} from '@angular/core';
import {NotificationsService} from '../services/notifiaction_service';
import {Loader} from './loader';
import Raven = require('raven-js');
import {Panel} from './panel';
import {AppState} from '../app.service';
import {Cookie} from './cookie';
import {TranslateClass} from './pipes/translate';
import {MockService} from '../services/mock';
import {UnavailableService} from '../services/unavailable-service';
import {CustomQueryEncoderHelper} from './query-encoder-helper';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';

declare var mainAction: string;
declare var logoutUrl: string;
declare var pingAction: string;

@Injectable()
export class XHR {
    request: HttpClient;
    mainAction: string;
    pingAction: string;
    formdata: HttpParams;
    addCsrfTokenFor: string[] = [Panel.DA];
    pingTimeout: number = 0;

    constructor(
        private http: HttpClient,
        private unavailableService: UnavailableService,
        private notifictions: NotificationsService,
        public appState: AppState,
        private mockService: MockService
    ) {
        this.request = http;
        this.mainAction = mainAction;
        this.pingAction = (typeof pingAction !== 'undefined') ? pingAction : '';
    }

    post(params: Object, mockJson = ''): Promise<any> {
        this.checkPluginAvailability();
        return new Promise((resolve, reject) => {
            let headers = new HttpHeaders();
            headers.append('Content-type', 'application/x-www-form-urlencoded');
            if (this.addCsrfTokenFor.indexOf(this.appState.get('panelName')) > -1) {
                params['csrftoken'] = Cookie.getCookie('csrftoken');
            }

            this.mockService.processRequest(mockJson, params);

            this.formdata = new HttpParams({encoder: new CustomQueryEncoderHelper()});
            this.appendToFormData(params, '');

            return this.request.post(this.mainAction, this.formdata, {headers: headers})
                .subscribe(
                    data => {
                        try {
                            let response: any = data;
                            if (response.result === 'success') {
                                if (response.hasOwnProperty('warning')) {
                                    let message = new TranslateClass().transform([
                                        response.warning,
                                        response.context || {}
                                    ]);
                                    console.log('Warning: ' + message);
                                    this.notifictions.warning('Warning', message);
                                }
                                resolve(response);
                            } else {
                                reject(response);
                            }
                        } catch (e) {
                            Loader.stopAllLoader();
                            this.notifictions.error('Error', e.message);
                            reject(data);
                            throw e;
                        }
                    },
                    err => {
                        console.log(err);
                        Raven.setExtraContext({
                            request: this.formdata.toString(),
                        });
                        Loader.stopAllLoader();

                        let error_object = Object();
                        try {
                            error_object = err.error;

                            if (this.checkLostAuthorization(error_object, err.status)) {
                                return;
                            }

                            if ([502, 503].includes(err.status)) {
                                if (error_object.result === 'License is not valid') {
                                    location.hash = '/license';
                                } else if (error_object.code === 503) {
                                    this.unavailableService.showPage(error_object);
                                    return;
                                } else {
                                    this.showErrorMessage(error_object);
                                }
                            } else {
                                this.showErrorMessage(error_object);
                            }

                            if (this.checkLogoutSignal(error_object)) {
                                if (typeof logoutUrl !== 'undefined') {
                                    this.handleLogoutError(logoutUrl);
                                }
                            }
                        } catch (e) {
                            this.notifictions.error('Error', e.message);
                        }
                        reject(error_object);
                        if (typeof error_object.result !== 'undefined') {
                            throw error_object.result;
                        } else {
                            throw err;
                        }
                    },
                    () => {
                        if (this.pingTimeout) {
                            window.clearTimeout(this.pingTimeout);
                        }
                    }
                );
        });
    }

    private checkLostAuthorization(error_object, status) {
        if (
            (error_object.text && this.isLogoutError(error_object.text)) // Plesk
            || !status || status === 401 // cPanel
        ) {
            this.handleLogoutError();
            return true;
        }
        return false;
    }

    private showErrorMessage(error_object) {
        if (!error_object) this.notifictions.error('Error', 'ERROR.undefined');
        let message = new TranslateClass().transform([
            error_object.result,
            error_object.context || error_object.link || {}
        ]);
        if (error_object.hasOwnProperty('details')) {
            console.log(
                'Error on server: ' + message,
                error_object.details
            );
            this.notifictions.error(
                'Error', message, error_object.details
            );
        } else {
            console.log('Error on server: ' + message);
            this.notifictions.error('Error', message);
        }
    }

    private appendToFormData(params: Object, arrayName: string) {
        if (Array.isArray(params)) {
            for (let elem of params) {
                this.formdata = this.formdata.append(arrayName, elem);
            }
        } else {
            for (let index in params) {
                let value = params[index];
                if (typeof(value) === 'object') {
                    this.appendToFormData(value, index);
                } else {
                    let key = index;
                    if (!isNaN(Number(index))) {
                        index = '';
                    }
                    if (arrayName !== '') {
                        key = arrayName + '[' + index + ']';
                    }

                    this.formdata = this.formdata.set(key, value);
                }
            }
        }
    }

    private handleLogoutError(href?: string) {
        if (this.pingTimeout) {
            window.clearTimeout(this.pingTimeout);
        }
        this.notifictions.error('Platform error', 'ERROR.expired_session');

        window.setTimeout(() => {
            Cookie.deleteCookie('csrftoken');
            if (href) {
                document.location.href = href;
            } else {
                document.location.reload(true);
            }
        }, 5000);
    }

    private isLogoutError(_body) {
        if (typeof(_body) === 'string') {
            return /<html.*>.*<\/html>/.test(_body.replace(/\r?\n|\r/g, ' '));
        }
        return false;
    }

    private checkLogoutSignal(response) {
        try {
            if (response.hasOwnProperty('logoutSignal')) {
                return response['logoutSignal'];
            }
        } catch (e) {
            // Do nothing
        }
        return false;
    }

    private toParamString(params: Object, arrayName: string): string {
        let bodyParams = [];
        for (let index in params) {
            let value = params[index];
            if (typeof(value) === 'object') {
                bodyParams.push(this.toParamString(value, index));
            } else {
                let key = index;
                if (arrayName !== '')
                    key = arrayName + '[' + index + ']';

                bodyParams.push(key + '=' + value);
            }
        }
        return bodyParams.join('&');
    }

    public mapToJson(map) {
        let request_obj = {};
        map.forEach( (value, key, map) => {
            request_obj[`${key}`] = value;
        });
        return JSON.stringify(request_obj);
    }

    public checkPluginAvailability() {
        if (this.pingAction) { // ping Action will be specified only for cPanel
            this.pingTimeout = window.setTimeout(() => {
                this.request.get(this.pingAction)
                    .subscribe(
                        data => {
                            // console.log('===>', 'do nothing');
                        },
                        err => {
                            if (!err.status || err.status === 401) {
                                this.handleLogoutError();
                            }
                        }
                    );
            }, 10000);
        }
    }
}
