import { Router } from 'aurelia-router';
import { User } from './../../../../model/common-model';
import { BindingEngine, Disposable } from 'aurelia-framework';
import { AnchorTab } from '../../../../ui/anchor-tab/ui-anchor-tab';
import { DialogBoxViewModel } from '../../../../modules/dialogs/dialog-box';
import { Box, ShowEditDialogOptions } from '../../../../modules/box';
import { CustomLogger } from '../../../../modules/custom-logger';
import { Datacontext } from '../../../../data/data-context';
import { ServiceBase } from '../../../../data/service-base';
import { EntityTypeNames } from '../../../../constants/constants';
import { autoinject } from 'aurelia-dependency-injection';
import { bindable, customElement } from 'aurelia-templating';
import { I18N } from 'aurelia-i18n';
import { GridOptions } from '../../utilities/ag-grid-helpers';
import { AgGridViewDetail } from '../../ag-grid-view-detail';
import { AgGridViewService } from '../../../../services/ag-grid-view-service';
import { AgGridView, AgGridViewUser, ActionDialogBoxInputParameters, ViewVisibilityEnum } from '../../../../model/common-model';
import { IMenuGroup, IMenuItem, IMenuItems } from '../../../../ui/menu/ui-menus';
import { AuthService } from '../../../../security/auth-service';
import { Config } from '../../../../config';
import { DialogCloseResult, DialogOpenResult } from 'aurelia-dialog';
import { KeyboardShortcut } from './../../../../constants/constants';

export interface AgGridViewFilter {
  name: string;
  agGridView: AgGridView;
  visibility: string;
  menuItems: Array<IMenuItems>;
}

@autoinject
@customElement('ag-grid-view-list')
export class AgGridViewList {
  @bindable showAllView: boolean;
  @bindable gridOptions: GridOptions;
  @bindable defaultAgGridView: AgGridView;
  @bindable fixedFiltersObserverDisabled: boolean;
  @bindable showFilterBox: boolean;
  @bindable datacontext: Datacontext;
  @bindable private agGridSearch: string;
  @bindable
  public agGridViewList: Array<AgGridView>;
  agGridViewListChanged(newValue, oldValue) {
    this.viewsConstruct();
    if (newValue != null && newValue != oldValue) {
      this.addAgGridViewListObserver();
    }
  }

  @bindable setAgGridView: (agGridView) => void;
  @bindable afterSetAgGridView: (agGridView) => void;

  private allowSharedViews: boolean = false;
  private viewsSeparatedByVisibility: boolean = false;
  private showViewVisibiliyInList: boolean = true;
  private showSystemViewOnlyAsPublicView = true;
  private allViews: Array<AgGridViewFilter> = [];
  private privateViews: Array<AgGridViewFilter> = [];
  private publicViews: Array<AgGridViewFilter> = [];
  private sharedViews: Array<AgGridViewFilter> = [];
  private tabList: Array<AnchorTab>;
  public disposables: Array<Disposable> = [];

  private gridViewService: AgGridViewService;
  private agGridViewUserService: ServiceBase<AgGridViewUser>;

  constructor(
    private i18n: I18N,
    private config: Config,
    private authService: AuthService,
    private box: Box,
    private logger: CustomLogger,
    private bindingEngine: BindingEngine,
    private router: Router,
    protected element: Element
  ) {
    this.allowSharedViews = this.config?.agGridConfig?.allowSharedView;
    this.tabList = [
      new AnchorTab('Private', this.i18n.tr('common:aggridview.private'), true),
      new AnchorTab('Shared', this.i18n.tr('common:aggridview.shared'), this.allowSharedViews),
      new AnchorTab('Public', this.i18n.tr('common:aggridview.public'), true),
    ];

    this.gridViewService = new AgGridViewService();
    this.agGridViewUserService = new ServiceBase<AgGridViewUser>(EntityTypeNames.AgGridViewUser);

    this.viewsSeparatedByVisibility = this.config?.agGridConfig?.separateViewsByVisibility ?? false;
    this.showViewVisibiliyInList = this.config?.agGridConfig?.repeatViewVisibilityInListItem ?? true;
    this.showSystemViewOnlyAsPublicView = this.config?.agGridConfig?.showSystemViewOnlyAsPublicView ?? true;
  }

  bind() {
    if (this.agGridViewList != null) {
      this.viewsConstruct();
      this.addAgGridViewListObserver();
    }
  }

  public detached() {
    if (this.disposables?.length) {
      this.disposables.forEach((x) => {
        x.dispose();
      });
    }
  }

  private addAgGridViewListObserver() {
    this.disposables.push(
      this.bindingEngine.collectionObserver(this.agGridViewList).subscribe((changeRecords) => {
        if (changeRecords?.length > 0) {
          this.viewsConstruct();
        }
      })
    );
  }

  public viewsConstruct() {
    this.allViews?.splice(0);
    this.privateViews?.splice(0);
    this.publicViews?.splice(0);
    this.sharedViews?.splice(0);
    for (let agGridView of this.agGridViewList) {
      if (agGridView != null) {
        let menuItems = new Array<IMenuItems>();
        let canModifyView = this.canModifyAgGridView(agGridView);
        if (canModifyView) {
          menuItems.push(...this.menuItemAuthor(agGridView));
        } else {
          if (!agGridView.isDefaultView) {
            menuItems.push(this.menuItemDuplicate(agGridView));
          }
        }
        let newViewFilter = {
          name: agGridView.name,
          agGridView: agGridView,
          visibility: this.getVisibilityTranslation(agGridView.visibility),
          menuItems: menuItems,
        } as AgGridViewFilter;
        switch (agGridView.visibility) {
          case 'Public':
            this.publicViews.push(newViewFilter);
            break;
          case 'Private':
            this.privateViews.push(newViewFilter);
            break;
          case 'Shared':
            if (this.allowSharedViews) {
              this.sharedViews.push(newViewFilter);
            }
            break;
          case 'System':
            this.publicViews.push(newViewFilter);
            if (!this.showSystemViewOnlyAsPublicView) {
              this.privateViews.push(newViewFilter);
              if (this.allowSharedViews) {
                this.sharedViews.push(newViewFilter);
              }
            }
            break;
        }
        this.allViews.push(newViewFilter);
      }
    }
  }

  private canModifyAgGridView(agGridView: AgGridView): boolean {
    if (this.showAllView) {
      return true;
    } else if (
      agGridView.visibility == 'System' &&
      this.config.agGridConfig.administratorRoles != null &&
      this.authService.currentUser?.roles != null &&
      this.authService.currentUser.roles.some((role) => this.config.agGridConfig.administratorRoles.includes(role))
    ) {
      return true;
    }
    return this.authService.currentUser?.id == agGridView.authorId && (agGridView.visibility == 'Public' || agGridView.visibility == 'Private' || agGridView.visibility == 'Shared');
  }

  private menuItemAuthor(agGridView: AgGridView): Array<IMenuItems> {
    let menuItemsAuthor = [
      {
        group: '1',
        hiddenLabel: true,
        items: [
          {
            label: this.i18n.tr('common:general.edit'),
            icon: 'digi-edit',
            handler: async () => {
              await this.modifyAgGridView(agGridView);
            },
          },
          !agGridView.isDefaultView ? this.menuItemDuplicate(agGridView) : null,
          agGridView.visibility == 'Private' && this.showAllView ? this.menuItemCanShowAll(agGridView) : null,
        ],
      } as IMenuGroup,
    ] as Array<IMenuItems>;

    if (!this.isDefaultView(agGridView)) {
      menuItemsAuthor.push({
        label: this.i18n.tr('common:general.remove'),
        icon: 'digi-trash',
        handler: async (data) => {
          await this.deleteAgGridView(agGridView);
          this.viewsConstruct();
        },
      } as IMenuItem);
    }
    return menuItemsAuthor;
  }

  private menuItemDuplicate(agGridView: AgGridView): IMenuItem {
    return {
      label: this.i18n.tr('common:general.duplicate'),
      icon: 'digi-copy',
      handler: async (data) => {
        await this.duplicateAgGridView(agGridView);
        this.viewsConstruct();
      },
    } as IMenuItem;
  }

  private menuItemCanShowAll(agGridView: AgGridView): IMenuItem {
    return {
      label: this.i18n.tr('common:general.specificCopy'),
      icon: 'digi-copy',
      handler: async (data) => {
        await this.copyAgGridView(agGridView);
        this.viewsConstruct();
      },
    } as IMenuItem;
  }

  private getVisibilityTranslation(visibility: ViewVisibilityEnum) {
    if (visibility == 'Private') {
      return this.i18n.tr('common:aggridview.private');
    } else if (visibility == 'Public') {
      return this.i18n.tr('common:aggridview.public');
    } else if (visibility == 'Shared') {
      return this.i18n.tr('common:aggridview.shared');
    } else if (visibility == 'System') {
      return '';
    }
  }

  private async modifyAgGridView(agGridView: AgGridView) {
    if (this.canModifyAgGridView(agGridView)) {
      let buttonSave: ActionDialogBoxInputParameters = {
        label: this.i18n.tr('general.save', { ns: 'common' }),
        title: this.i18n.tr('general.save', { ns: 'common' }),
        theme: 'primary',
        type: 'solid',
        disabled: false,
        fn: (thisBox: DialogBoxViewModel) => {
          thisBox.controller.ok();
        },
        keyboardShortcut: this.config.globalConfig.keyboardShortcutFwBox ? KeyboardShortcut.Enter : null
      };
      let buttonCancel: ActionDialogBoxInputParameters = {
        label: this.i18n.tr('general.cancel', { ns: 'common' }),
        title: this.i18n.tr('general.cancel', { ns: 'common' }),
        theme: 'dark',
        type: 'ghost',
        disabled: false,
        fn: (thisBox: DialogBoxViewModel) => {
          thisBox.controller.cancel();
        },
        keyboardShortcut: this.config.globalConfig.keyboardShortcutFwBox ? [KeyboardShortcut.Escape, KeyboardShortcut.Backspace] : null
      };
      this.box.showQuestion(this.i18n.tr('general.doYouWantToSave', { ns: 'common' }), this.i18n.tr('view.view', { ns: 'common' }), [buttonCancel, buttonSave]).then((selectedOption) => {
        selectedOption.closeResult.then(async (result) => {
          if (!result.wasCancelled) {
            let isDefaultModified: boolean = this.isDefaultView(agGridView);
            if (isDefaultModified) {
              let defaultView = await this.gridViewService.createEntity({
                name: agGridView.name,
                moduleId: agGridView.moduleId,
                visibility: agGridView.visibility,
                authorId: agGridView.authorId,
                isDefaultView: true,
              });
              this.agGridViewList.splice(this.agGridViewList.indexOf(agGridView), 1, defaultView);
              agGridView = defaultView;
            }
            agGridView.columnState = JSON.stringify(this.gridOptions.columnApi.getColumnState());
            agGridView.filterModel = JSON.stringify(this.gridOptions.api.getFilterModel());
            agGridView.fixedFilters = this.gridOptions.fixedFilters ?? false;
            agGridView.adjustColumns = this.gridOptions.adjustColumns ?? this.config.agGridConfig.defaultAdjustColumns;
            await this.gridViewService.saveEntity(agGridView, true);
            this.logger.Log(this.i18n.tr('grid.viewModified', { ns: 'common' }), null, 'CustomAgGrid', true);
            if (isDefaultModified) {
              this.setAgGridView(agGridView);
            }
          }
        });
      });
    }
  }

  private async deleteAgGridView(agGridView: AgGridView) {
    if (this.canModifyAgGridView(agGridView)) {
      if (agGridView.agGridViewUsers?.length > 0) {
        await this.agGridViewUserService.deleteEntities([...agGridView.agGridViewUsers], true, null);
      }
      let isDeleted: boolean = await this.gridViewService.deleteEntities([agGridView], true);
      if (isDeleted) {
        if (agGridView.isDefaultView) {
          if (this.defaultAgGridView == null) {
            this.defaultAgGridView = await this.gridViewService.createEntity({
              id: -100,
              name: this.i18n.tr('general.default', { ns: 'common' }),
              moduleId: this.router.currentInstruction.config.moduleId,
              fixedFilters: false,
              adjustColumns: this.config.agGridConfig.defaultAdjustColumns,
              columnState: JSON.stringify(this.gridOptions.columnApi.getColumnState()),
              filterModel: null,
              sortModel: null,
              visibility: 'System',
              author: null,
              authorId: this.authService.currentUser.id,
              isDefaultView: true,
              createdBy: null,
              createdById: null,
              createdTime: null,
              updatedBy: null,
              updatedById: null,
              updatedTime: null,
              entityAspect: null,
              entityType: null,
              hide: false,
              used: false,
            });
          }
          this.agGridViewList.unshift(this.defaultAgGridView);
          this.defaultAgGridView.entityAspect.setDetached();
        }

        this.agGridViewList.remove(agGridView);
        this.logger.Log(this.i18n.tr('grid.viewDeleted', { ns: 'common' }), null, 'CustomAgGrid', true);

        if (agGridView.used) {
          this.setAgGridView(this.defaultAgGridView);
        }
      }
    }
  }

  private async duplicateAgGridView(agGridView: AgGridView) {
    await this.box
      .showEditDialog(
        AgGridViewDetail,
        -100,
        this.i18n.tr('common:grid.duplicate', { ns: 'common' }),
        new ShowEditDialogOptions({
          callback: async (createdEntity: AgGridView) => {
            (createdEntity.name = agGridView.name + '_Copy'),
              (createdEntity.moduleId = agGridView.moduleId),
              (createdEntity.fixedFilters = agGridView.fixedFilters),
              (createdEntity.adjustColumns = agGridView.adjustColumns),
              (createdEntity.columnState = agGridView.columnState),
              (createdEntity.filterModel = agGridView.filterModel),
              (createdEntity.visibility = agGridView.visibility),
              (createdEntity.agGridViewUsers = await this.duplicateAgGridViewUser(agGridView, createdEntity));
          },
          lock: false,
          containerClass: 'container-ag-grid-view-detail',
          overlayClass: 'overlay-ag-grid-view-detail',
          size: 'sm',
        })
      )
      .whenClosed((result) => {
        if (!result.wasCancelled) {
          let createdAgGridView: AgGridView = result.output;
          this.agGridViewList.push(createdAgGridView);
          this.setAgGridView({ agGridView: createdAgGridView });
        }
      });
  }

  private async duplicateAgGridViewUser(oldAgGridView: AgGridView, newEntity: AgGridView) {
    if (oldAgGridView.agGridViewUsers != null) {
      for (let agGridViewUser of oldAgGridView.agGridViewUsers) {
        let newAgGridViewUser: AgGridViewUser = await this.agGridViewUserService.createEntity();
        newAgGridViewUser.agGridView = newEntity;
        newAgGridViewUser.userId = (agGridViewUser as AgGridViewUser).userId;
        newAgGridViewUser.user = (agGridViewUser as AgGridViewUser).user;
      }
    }
    return newEntity.agGridViewUsers;
  }

  private async copyAgGridView(agGridView: AgGridView) {
    await this.box
      .showSelect(this.i18n.tr('grid.createdById', { ns: 'common' }), this.i18n.tr('grid.userSelection', { ns: 'common' }), new ServiceBase<User>('User'), 'fullName', 'id', 'digi-copy')
      .then((dialogOpenResult: DialogOpenResult) => {
        dialogOpenResult.closeResult.then(async (dialogCloseResult: DialogCloseResult) => {
          if (!dialogCloseResult.wasCancelled) {
            let userIds: Array<number> = dialogCloseResult.output;
            let copiedAgGridViews: Array<AgGridView> = new Array<AgGridView>();
            for (let userId of userIds) {
              let copiedAgGridView: AgGridView = await this.gridViewService.createEntity({
                name: agGridView.name,
                moduleId: agGridView.moduleId,
                fixedFilters: agGridView.fixedFilters,
                adjustColumns: agGridView.adjustColumns,
                columnState: agGridView.columnState,
                filterModel: agGridView.filterModel,
                visibility: 'Private',
                authorId: userId,
              });
              copiedAgGridViews.push(copiedAgGridView);
            }
            await this.gridViewService.saveEntities(copiedAgGridViews);
            this.agGridViewList.push(...copiedAgGridViews);
            this.logger.Log(this.i18n.tr('grid.viewCopied', { ns: 'common' }), null, 'CustomAgGrid', true);
          }
        });
      });
  }

  private isDefaultView(agGridView: AgGridView) {
    return agGridView != null && agGridView.isDefaultView && agGridView.id == -100;
  }
}
