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

import { Component, ViewChild, Input, Output, EventEmitter } from '@angular/core';
import { Grid } from './grid';
import { XHR } from './xhr';
import { User } from '../model/user';
import { UsersRepo } from '../repository/users_repo';
import { StatsRepo } from '../repository/stats_repo';
import { ModalStats } from  './modal_stats';
import { AppState } from '../app.service';
import { ViewModes } from '../services/view_modes';
import { Access } from '../services/access';
import { TranslateService } from '@ngx-translate/core';
import { Column } from './column';
import { ModalLimitsUser } from './modal_limits_user';
import { Cell, Field } from './row';
import { FieldProperties } from './types';


@Component({
  selector: 'users_grid',
  template: require('./../../templates/users_grid.html'),
  providers: [XHR, UsersRepo, StatsRepo],
  styles: [
    require('../../styles/users.grid.less'),
  ],
})

export class UsersGrid extends Grid{
  originRows: Array<User>;

  @Input('rows') rows: Array<User>;
  @Input('columns') columns: Array<Column>;
  @Input('rowsOnPage') rowsOnPage: any;
  @Input('loaderSelector') loaderSelector: any;
  @Input('globalCageFsStatus') globalCageFsStatus: boolean;
  @Input('globalMySqlGov') globalMySqlGov: boolean;

  @ViewChild(ModalStats) ModalStats: ModalStats;
  @ViewChild(ModalLimitsUser) ModalLimits: ModalLimitsUser;

  @Output() updateAllUsers: EventEmitter<any> = new EventEmitter();

  constructor(
      private usersRepo: UsersRepo,
      public appState: AppState,
      private viewModeService: ViewModes,
      private accessService: Access,
      private translate: TranslateService
  ) {
    super(appState);
  }

  /**
   * Show or hide all not the first lines of cell.
   *
   * @param row
   * @param expand
   */
  public toggleExpand(row: User, expand: boolean){
      let cells = row.cells;
      for (let field of Object.keys(cells)) {
          let lines = cells[field].lines;
          for (let i = 1; lines.length > 1 && i < lines.length; i++) {
              lines[i].hidden = !expand;
          }
      }
      row.expand = expand;
  }

  edit(row: User) {
      row.restoreCells();
      this.loadDataIntoModalWindow(row);
  }

  loadDataIntoModalWindow(row: User) {
      this.ModalLimits.loadEditing(row, this);
      this.ModalLimits.loading = false;
  }

  restoreAfterEdit(row: User){
      this.toggleExpand(row, row.expand);
      row.loadingStage = false;
  }

  public async save(row: User) {
      try {
          let res = await row.save();
          await this.updateUserRow(row, res);
          if (row.id === 'DEFAULT') {
              this.updateAllUsers.emit();
          }
      } catch (e) {
          this.restoreAfterEdit(row);
      }
  }

  public toggleCageFS(user: User) {
    let newCageFs = (user.cagefs === 'enabled') ? 'disabled' : 'enabled';
    return this.saveUserWithParams(
        user,
        {'cagefs': newCageFs},
        () => this.updateUserRow(user, {users: [{cageFS: newCageFs}]}));
  }

  public toggleMySQL(user: User) {
    let newMySQLGov = (user.mysql_gov === 'ignored') ? 'watched' : 'ignored';
    return this.saveUserWithParams(
        user,
        {'mysql-gov': newMySQLGov},
        () => this.updateUserRow(user, {users: [{mySqlGov: newMySQLGov}]}));
  }

  public reset(user: User) {
      return this.saveUserWithParams(
          user,
          {'default': 'all'},
          (res) => this.updateUserRow(user, res));
    }

  public doNotLimit(user: User) {
      return this.saveUserWithParams(
          user,
          {'unlimited': ''},
          (res) => this.updateUserRow(user, res));
  }

  public switchLimitsForReseller(status: string, user: User) {
      user.loadingStage = true;
      return new Promise((resolve, reject) => {
          this.usersRepo[status + 'LimitsForReseller'](user.username)
              .then(res => {
                  return this.updateUserRow(user).then(resolve).catch(reject);
              })
              .catch(() => {
                  user.loadingStage = false;
                  reject();
              });
      });
  }

    public history(user: User) {
        this.loading = true;
        let userId: number = user.userType === 'reseller' ?
            + Math.pow(10, 9) + Number(user.id) : +user.id;

        this.ModalStats.loadHistory(userId, true, '10m').then(() => {
            this.loading = false;
        });
    }

    /**
     * For saving user parameters and resolve following actions (catching errors, updating modal)
     * @param {User} user
     * @param params Parameters for saveUser request
     * @param cb callback which is called after user saved, returning result of save request
     * @returns {Promise<void>}
     */
    private async saveUserWithParams(user: User, params: any, cb: any) {
        try {
            user.loadingStage = true;
            let response = await this.usersRepo.saveUser(user, params);
            await cb(response);
        } catch (e) {
            this.loadDataIntoModalWindow(user);
        } finally {
            user.loadingStage = false;
        }
    }

  private getSpan(row: User, col: Column, lineNumber: number): number {
      let cell = row.cells[col.name];
      let userTableRow = [];
      if (cell) {
          this.columns.forEach((column, index) => {
              let cellObject = column.name !== 'actions'
                  ? row.cells[column.name]
                  : new Cell([
                      new Field(<FieldProperties>{value: 'actions'})
                  ]);
              userTableRow.push({
                  value: cellObject ? cellObject.lines[lineNumber] : undefined,
                  name: column.name,
                  index: index
              });
          });
      }
      let maxIndex = Math.max(...userTableRow.filter(o => !!o.value && !!o.value.altValue)
          .map(o => o.index));
      let currentValue = userTableRow.find(o => o.index === maxIndex) || {};
      return col.name === currentValue.name ? this.columns.length - maxIndex : 1;
  }

  private isVisible(row: User, lineNumber: number, colNum: number): boolean {
      let userTableRow = [];
      this.columns.forEach((column: Column, index: number) => {
          let cellObject = column.name !== 'actions'
              ? row.cells[column.name]
              : new Cell([
                  new Field(<FieldProperties>{value: 'actions'})
              ]);
          userTableRow.push({
              value: cellObject ? cellObject.lines[lineNumber] : undefined,
              name: column.name,
              index: index
          });
      });
      let maxIndex = Math.max(...userTableRow.filter(o => !!o.value && !!o.value.altValue)
          .map(o => o.index));
      return colNum - 1 < maxIndex;
  }

  private async updateUserRow(row: User,  data: any = {}): Promise<any> {
      try {
          if (data.users || data.resellers) {
              if (data.resellers && row.isReseller) {
                  data.users = data.resellers;
                  delete data.resellers;
              }
              this.accessService.dropPermission(data, data.users[0]);
              row.loadData(data, 0, true);
          } else {
              row.loadingStage = true;
              let res = await this.usersRepo.getUser(Number(row.id), row);
              if (this.appState.get('isAdmin')) {
                  if (res.resellers && res.resellers.length > 0) {
                      // drop reseller names for recheck status
                      this.viewModeService.resellerNames =
                          this.viewModeService.resellerNames.filter(resellerName => {
                              Object.keys(res.resellers).includes(resellerName);
                          });
                  }
                  this.viewModeService.concatResellerWithUser(res);
                  this.viewModeService.detectUserTypes(res, ViewModes.availableModes);
              }
              if (res.users && row.isReseller) {
                  this.updateAllUsers.emit({response: res});
              }
              this.accessService.dropPermission(res, res['users'][0]);
              if (res.resellers && row.isReseller) {
                  res.users = res.resellers;
                  delete res.resellers;
              }
              row.loadData(res, 0, row.wasExpand);
              this.loadDataIntoModalWindow(row);
          }
      } catch (e) {
          this.restoreAfterEdit(row);
          throw 'Can not fetch users data!';
      } finally {
          this.restoreAfterEdit(row);
          return;
      }
  }
}
