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

import { Component, OnInit, ViewChild } from '@angular/core';
import { Column } from './utils/column';
import { Row } from './utils/row';
import { SelectOption } from './utils/select_option';
import { XHR } from './utils/xhr';
import { BaseComponent } from './utils/base_component';
import { Helper } from './utils/helper';
import { Grid } from './utils/grid';
import { AppState } from './app.service';
import { ViewModes } from '../app/services/view_modes';
import { UsersRepo } from '../app/repository/users_repo';
import { User } from './model/user';
import { UserEditActions } from './utils/user_actions';
import { ModalLimitsUser } from './utils/modal_limits_user';
import { HIDE_MYUSQL_LIMITS_MODES } from '../app/services/constants';
import { shortenColumnsArray } from './utils/componentProperties/columnExtendedProperties';
import { PopoverContent, PopoverStatsData } from './utils/popover';
import { LoaderSelector } from './utils/loader';

declare var mainAction: any;
declare const panelName: any;

@Component({
    selector: '.current_block',
    template: require('./../templates/current.component.html'),
    providers: [
      XHR,
      ViewModes,
      UsersRepo,
      UserEditActions
    ],
    styles: [
      require('../styles/current.component.less'),
      require('../styles/popover.less')
    ]
})
export class CurrentComponent extends BaseComponent implements OnInit {
    static adminOnlyFields: Object = {
        'cpu_mysql': true,
        'io_mysql': true,
    };
    usage: Array<Usage>;
    originalUsage: Array<Usage>;
    columns: Array<Column> = [];
    refreshArray: SelectOption[];
    selectedRefresh: SelectOption;
    showTopArray: SelectOption[];
    selectedShowTop: SelectOption;
    refreshInterval: any;
    request: XHR;
    timeToRefresh: number;
    _disableAutoRefresh: boolean = false;
    pauseLabel: string;
    mysqlColumns = ['cpu_mysql', 'io_mysql'];
    popover_content: PopoverContent;
    lastResponse: any;
    _paused: boolean = false;
    _user_paused: boolean = false;
    popoverTimeout: number = 0;
    panel: string = panelName;
    fetching: boolean = false;
    visibleElements: any = {};
    grouped_header: boolean = false;
    mySqlGovMode: string = '';
    restrictedCount: number = 0;

    @ViewChild('grid') grid: Grid;

    @ViewChild(ModalLimitsUser) modalLimits: ModalLimitsUser;

    initVisibility() {
        if (this.appState.get('isAdmin')) {
            this.visibleElements.hideMysql = true;
        }else{
            this.visibleElements.hideMysql = false;
            this.grouped_header = true;
        }
    }

    get paused(): boolean{
        return this._paused;
    }

    set paused(value: boolean){
        if (value || this._disableAutoRefresh){
            clearInterval(this.refreshInterval);
            this.refreshInterval = 0;
            this.pauseLabel = 'Unpause';
        } else {
            clearInterval(this.refreshInterval);
            this.refreshInterval = setInterval(this.checkRefresh.bind(this), 1000);
            this.pauseLabel = 'Pause';
        }
        this._paused = value;
    }

    get disableAutoRefresh(): boolean{
      return this._disableAutoRefresh;
    }

    set disableAutoRefresh(value: boolean){
      this._disableAutoRefresh = value;
      this.paused = this._paused;
    }

    private _mysqlHide: boolean;
    set mysqlHide(value: boolean){
        this._mysqlHide = value;

        for (let id in this.columns){
            let column = this.columns[id];
            if (column && this.mysqlColumns.includes(column.name)){
                column.hidden = value;
            }
        }
    }

    get mysqlHide(): boolean {
        if (this.visibleElements.hideMysql ){
          return this._mysqlHide;
        }
        return false;
    }

    // Filter options
    private _filterField: string;
    set filterField(value: string) {
        this._filterField = value;
        if (typeof this.originalUsage !== 'undefined') {
          this.doFilter();
        }
    }
    get filterField(): string {
        return this._filterField;
    }

    private _filterBy: string = 'username'; // column name
    set filterBy(value: string) {
        this._filterBy = value;
        this.doFilter();
    }
    get filterBy(): string {
        return this._filterBy;
    }

    constructor(public appState: AppState,
                private xhr: XHR,
                private viewModeService: ViewModes,
                private userEditActions: UserEditActions,
                private usersRepo: UsersRepo) {
        super();

        this.request = xhr;
        this.mysqlHide = false;
        this.refreshArray = [
            {name: '5 ', value: 5},
            {name: '10 ', value: 10},
            {name: '15 ', value: 15},
            {name: '30 ', value: 30},
            {name: '60 ', value: 60}
        ];
        this.selectedRefresh = this.refreshArray.find(item => item.value === 10);
        this.timeToRefresh = this.selectedRefresh.value;

        this.showTopArray = [
            {name: '10', value: 10},
            {name: '25', value: 25},
            {name: '50', value: 50},
            {name: '100', value: 100}
        ];
        this.selectedShowTop = this.showTopArray.find(item => item.value === 25);

        this.load().then((response: any) => {
            if (response.mySqlGov !== 'enabled') {
                this.mysqlHide = true;
            }
        });
        // this.refreshInterval = setInterval(this.checkRefresh.bind(this), 1000);
        this.paused = false;
        this.popover_content = {
            id: '',
            username: '',
            domain: '',
            stats: [],
            hidden: true,
            top: '10px',
            left: '10px',
        };

        this.pauseLabel = 'Pause';

        this.initVisibility();
        userEditActions.saveCallback = (user: User) => this.save(user);
        userEditActions.closeCallback = (user: User) => this.closeEdit(user);
    }

    ngAfterViewInit() {
        this.loaderSelector = new LoaderSelector('.current_block');
        this.loading = true;

        let component = this;
        document.getElementsByTagName('popover')[0].setAttribute('hidden', '');

        function mouseleaveFromGrid(e){
            component.hidePopover();
        }

        document.getElementById('current_grid').addEventListener('mouseover', function(e){
            let element = <Element>e.target;
            if (element.classList.contains('grid_td_username')
                || element.classList.contains('grid_td_domain')
            ) {
                let textElement = element.querySelector('span:last-child');
                if (!textElement) {
                    textElement = element.querySelector('span + i:last-child');
                    if (!textElement) {
                        return;
                    }
                }
                clearTimeout(component.popoverTimeout);
                let el = document.getElementsByTagName('popover');
                if (el.length && el[0]){
                    el[0].removeAttribute('hidden');
                    e.target.appendChild(el[0]);
                }
                let padding = parseInt(window.getComputedStyle(element, null)
                    .getPropertyValue('padding-left'), 10);
                let htmlTextElement = <HTMLElement><any>textElement;
                component.popover_content.left = (
                    htmlTextElement.offsetLeft + htmlTextElement.offsetWidth + padding) + 'px';

                component.popover_content.top = '15px';

                component.showPopover(parseInt(element.closest('tr')
                    .getAttribute('data-user-id'), 10));
                component.paused = true;

                e.target.removeEventListener('mouseleave', mouseleaveFromGrid);
                e.target.addEventListener('mouseleave', mouseleaveFromGrid);
            }
        });
    }

    hidePopover() {
        this.popover_content.hidden = true;
        if (!this._user_paused)
            this.paused = false;
        document.getElementById('popover_block')
            .appendChild(document.getElementsByTagName('popover')[0]);
        document.getElementsByTagName('popover')[0]
            .setAttribute('hidden', '');
    }

    showPopover(userId: number) {
        let user = this.lastResponse.users.find(row => row.id === userId);
        let original = this.usage.find(row => row.user_id === userId);
        this.popover_content.id = user.id;
        this.popover_content.username = user.username;
        this.popover_content.domain = user.domain;
        this.popover_content.restricted = original.restricted;
        this.popover_content.restriction = user.restriction;
        let stats: Array<PopoverStatsData> = [];

        for (let column of shortenColumnsArray) {
            if (this.hasColumn(column.name)) {
                let name: string, isChild: boolean = false, hasChild: boolean = false;
                let usage: number | string, limits: number | string;
                let sqlColumn: boolean = column.name.includes('mysql');

                if (sqlColumn && this.mysqlHide ) continue;

                if (column.group){
                    // TODO find another way to map column and user key
                    let groupKey = column.group === 'SPEED' ? 'cpu' : column.group.toLowerCase();
                    if (sqlColumn){
                        if (this.mysqlHide || typeof user.usage[groupKey].mysql !== 'number')
                            continue;
                        name = `MySQL ${column.group}`;
                        isChild = true;
                        usage = user.usage[groupKey].mysql;
                        limits = user.limit[groupKey].mysql;
                    } else {
                        name = column.group;
                        hasChild = !this.mysqlHide;
                        usage = user.usage[groupKey].all;
                        limits = user.limit[groupKey].all;
                    }
                }
                else {
                    name = column.descr;
                    usage = user.usage[column.descr.toLowerCase()];
                    limits = user.limit[column.descr.toLowerCase()];
                }
                stats.push({
                    name,
                    hasChild,
                    isChild,
                    usage: column.units ? Helper.reformatValue(usage, column.units).value :
                        this.safeToFixed(usage, 2),
                    limits: column.units ? Helper.reformatValue(limits, column.units).value :
                        limits
                });
            }
        }

        this.popover_content.stats = stats;
        this.popover_content.hidden = false;
    }


    routerCanDeactivate() {
        clearInterval(this.refreshInterval);
        this.refreshInterval = 0;
        return true;
    }

    load() {
        return new Promise((resolve, reject) => {
            this.disableAutoRefresh = true;
            this.fetching = true;
            if (this.popover_content
                && this.popover_content.hasOwnProperty('hidden')
                && !this.popover_content.hidden
            ) {
                this.hidePopover();
            }
            this.loading = true;
            this.request.post({
                command: 'cloudlinux-top',
                params: {
                    max: 100
                }
            })
            .then((response: any) => {
                this.lastResponse = response;

                this.viewModeService.concatResellerWithUser(response);

                if (this.appState.get('isAdmin')) {
                    this.viewModeService.detectUserTypes(response, this.viewModes);
                }
                this.mySqlGovMode = response.mySqlGovMode;
                this.originalUsage = this.parseUsage(response);
                this.doFilter();
                this.loading = false;
                this.fetching = false;
                this.disableAutoRefresh = false;
                resolve(response);
            })
            .catch(() => {
                this.loading = false;
                this.fetching = false;
                this.disableAutoRefresh = false;
                reject();
            });
        });
    }

    togglePause(){
        this.paused = !this.paused;
        this._user_paused = this.paused;
    }


    checkRefresh(){
        this.timeToRefresh--;
        if (this.timeToRefresh === 0) {
            this.load();
            this.timeToRefresh = this.selectedRefresh.value;
        }
    }

    parseUsage(response){
        let list = [];
        for (let i = 0; i < response.users.length; i++){
            let user = response.users[i];
            let row = new Usage();
            row.username = user.username;
            row.id = user.id;
            row.user_id = user.id;
            row.package = user.package;
            row.domain = user.domain;
            row.hasStar = user.username === this.appState.get('userName') && user.isReseller;
            // hasHistory: this.appState.get('isReseller'),
            row.hasEdit = this.appState.get('isAdmin') ||
                !(user.username === this.appState.get('userName') && user.isReseller) ;
            row.userType = user.userType;
            row.isReseller = user.isReseller;
            row.reseller = user.reseller;
            row.userTypeTooltip = user.userTypeTooltip;
            if (this.mySqlGovMode === 'all') {
                if (user.usage.cpu.mysql > user.usage.cpu.all) {
                    user.usage.cpu.mysql = user.usage.cpu.all;
                }
                if (user.usage.io.mysql > user.usage.io.all) {
                    user.usage.io.mysql = user.usage.io.all;
                }
            }
            if (HIDE_MYUSQL_LIMITS_MODES.includes(this.mySqlGovMode)) {
                user.limit.cpu.mysql = '-';
                user.limit.io.mysql = '-';
            }
            let notSafeColumns = {
                cpu_mysql: Helper.reformatValue(user.usage.cpu.mysql, 'percents'),
                io_mysql: Helper.reformatValue(user.usage.io.mysql, 'byteinsec'),
                iops: user.usage.iops,
                pno: user.usage.pno,
                ep: user.usage.ep,
                io_all: Helper.reformatValue(user.usage.io.all, 'byteinsec'),
                mem: Helper.reformatValue(user.usage.mem, 'bytes'),
                cpu_all: Helper.reformatValue(user.usage.cpu.all, 'percents'),
                restricted: user.restricted,
                restriction: user.restriction,
            };

            for (let name in notSafeColumns) {
                this.safeInsertColumn(name, notSafeColumns[name], row);
            }
            list.push(row);
        }
        return list;
    }

    safeInsertColumn(name: string, value: any, row: any){
        if (typeof value === 'number')
            row[name] = value.toString();
        else
            row[name] = value;
    }

    safeToFixed(value, precision: number){
      return typeof value === 'number' ? parseFloat(value.toFixed(precision)) : value;
    }

    getColumns(): Array<Column> {
        let isAdmin = this.appState.get('isAdmin');
        let isReseller = this.appState.get('isReseller');
        let resultArray: Array<Column> = [
            new Column({
                name: 'username', descr: (isAdmin ? 'ID' : 'LVE ID'), convertToFormat: 'string'
            })
        ];

        shortenColumnsArray.forEach(item => {
            if (!isAdmin) {
                if (CurrentComponent.adminOnlyFields.hasOwnProperty(item.name)) return;
                if (item.group) {
                    item = Object.assign(Object.create(null), item);
                    item.descr = item.group;
                    item.group = null;
                }
            }
            resultArray.push(new Column(item));
        });

        if (isAdmin || isReseller){
            resultArray.push(
                new Column({
                    name: 'actions', descr: 'Actions', convertToFormat: 'string'
            }).sortableDisable());
        }
        return resultArray;
    }

    hasColumn(name): boolean {
        for (let col_num in this.columns){
            if (this.columns[col_num] && this.columns[col_num].name === name){
                return true;
            }
        }
        return false;
    }

    stringify(o: any): string {
        return JSON.stringify(o);
    }

    // TODO remove if not used
    updateSelectedValue(event, selector): void{
        var value = event.target.value;
        var el = JSON.parse(value);
        switch(selector){
            case 'selectedRefresh':
                this.selectedRefresh = el;
                this.timeToRefresh = this.selectedRefresh.value;
                break;
            case 'selectedShowTop':
                this.selectedShowTop = el;
                this.doFilter();
                break;
        }
    }

    doFilter: Function = () => {
        let rows: Array<any>;
        if (this.appState.get('isAdmin')) {
            rows = this.viewModeService.switchViewMode(
                this.availableViewModes,
                this.originalUsage);
        } else {
            rows = this.originalUsage;
        }

        this.usage = rows.filter(row => {
            return !this.filterField || row[this.filterBy].toString().match(this.filterField);
        }).slice(0, this.selectedShowTop.value);

        this.restrictedCount = rows.filter(row => row.restricted).length;
    };

    onRadioButtonClick(columnName: string, displayName: string) {
        let column = this.columns[0];
        if (column && this.grid.key === column.name){ // if default sorting is what we removing
            this.grid.key = '-'; // removing sorting
        }
        column.name = columnName;
        column.descr = displayName;
        this.filterBy = columnName;
        if ('-' === this.grid.key){ // if no sort order exists, that is default key value
            this.grid.formatBasedSort(column.convertToFormat, columnName);
        }
        this.columns = this.columns.slice(); // send event to #grid about column name changed
    }
    onUsernameRadioClick() {
        this.onRadioButtonClick('username', 'ID');
    }
    onDomainRadioClick() {
        this.onRadioButtonClick('domain', 'Domain');
    }
    /** Stop reloading after leaving page**/
    ngOnDestroy() {
        this.paused = true;
        return true;
    }

    ngOnInit() {
        this.columns = this.getColumns();
        this.grid.key = this.columns[0].name;
    }

    async unrestrictUser(id: number){
      let requiredUser = this.originalUsage.find(el => {
        return Number(el['id']) === id;
      });
      await this.setUsersUnrestricted({
        'lve-id': id,
        'mysql-restrict': 'unrestricted'
      });
      this.restrictedCount--;
      requiredUser.restricted = false;
    }

    async unrestrictAllUsers(){
      await this.setUsersUnrestricted({
        'mysql-unrestrict-all': ''
      });
      this.restrictedCount = 0;
      this.originalUsage.forEach(usage => {
        usage.restricted = false;
      });
    }

    actionClicked(event) {
        switch (event.action) {
            case 'edit':
                this.edit(event.row);
                break;
            case 'unrestrictUser':
                this.unrestrictUser(event.row.id);
                break;
            case 'unrestrictAllUsers':
                this.unrestrictAllUsers();
                break;
        }
    }

    edit(usageRow: Usage) {
        this.paused = true;
        this.loading = true;
        let userPromise;
        if ( usageRow.isReseller ) {
            userPromise = this.usersRepo.loadReseller(usageRow.username);
        } else{
            userPromise = this.usersRepo.loadUser(parseInt(usageRow.id, 10));
        }
        userPromise.then(({user, response}) => {
            usageRow.user = user;
            this.userEditActions.globalCageFsStatus = (response.cageFS === 'enabled');
            this.userEditActions.globalMySqlGov = (response.mySqlGov === 'enabled');
            this.modalLimits.loadEditing(user, this.userEditActions);
            this.loading = false;
            this.modalLimits.show();
        }).catch(() => {
            this.loading = false;
        });
    }

    async setUsersUnrestricted(params: object) {
        let wasPaused = this.paused;
        this.loading = true;
        this.paused = true;
        try {
            await this.request.post({
                command: 'cloudlinux-limits',
                method: 'set',
                params
            });
        } catch (requestError) {
            // Error should be processed already
        } finally {
            this.loading = false;
            if (!wasPaused) this.paused = false;
        }
    }

    private closeEdit(user: User) {
        if (!this._user_paused)
            this.paused = false;
        user.loadingStage = false;
    }

    public save(user: User) {
        user.save()
            .then(() => this.closeEdit(user))
            .catch(() => this.closeEdit(user));
    }
}

class Usage extends Row {
    id: string;
    user_id: number;
    restricted: boolean;
    username: string;
    domain: string;
    hasEdit: boolean = true;
    public user: User = null;
    public isReseller: boolean = null;
    package: string;
    userType: string;
    reseller: string;
    userTypeTooltip: string;
    restriction: any;

    get loadingStage(): boolean {
        if (this.user) {
            return this.user.loadingStage;
        }
        return false;
    }
}


