import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Component, Input, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core';
import { NzDrawerRef } from 'ng-zorro-antd/drawer';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { NzFormatEmitEvent, NzTreeComponent, NzTreeNodeOptions } from 'ng-zorro-antd/tree';
import { Observable, of, zip } from 'rxjs';
import { catchError, map, mergeMap, tap } from 'rxjs/operators';

/** 应用接口 */
interface Application {
  source: any;
  menuTreeNodeOptionList: NzTreeNodeOptions[];
  menuCheckedKeys: string[];
}

/** 提交结果 */
interface SubmitResult {
  success: boolean;
  updated: any;
}

/** 菜单选择器组件 */
@Component({
  selector: 'lib-nc-menu-selector',
  templateUrl: './nc-menu-selector.component.html',
  styleUrls: ['./nc-menu-selector.component.scss']
})
export class NcMenuSelectorComponent implements OnInit, OnDestroy {

  /** 角色 */
  @Input() role: any = null;

  /** 菜单树形组件列表 */
  @ViewChildren(NzTreeComponent) _menuTreeList!: QueryList<NzTreeComponent>;

  /** 应用列表 */
  _applicationList: Application[] = [];
  /** 应用加载状态 */
  _applicationLoading: boolean = false;
  /** 提交加载状态 */
  _submitLoading: boolean = false;

  /** 取消提交 */
  private _cancelSubmitFn = () => { };

  constructor(
    private _httpClient: HttpClient,
    private _nzNotificationService: NzNotificationService,
    private _nzDrawerRef: NzDrawerRef<NcMenuSelectorComponent, SubmitResult>) { }

  /** 初始化后 */
  ngOnInit(): void {
    this._getApplicationList();
  }

  /** 注销后 */
  ngOnDestroy(): void {
    this._cancelSubmitFn();
  }

  /**
   * 更新菜单树形组件状态
   * @param event 树节点事件
   */
  _updateMenuTreeStatus(event: NzFormatEmitEvent): void {
    const node = event.node!;
    if (node!.origin.source) {
      if (node!.origin.source.type === '权限' && node.parentNode!.isHalfChecked) {
        node.parentNode!.children
          .filter(node => /-view$/.test(node.key) && !node.isChecked)
          .forEach(node => node.isChecked = true);
      }
    } else {
      if (!node.isChecked && node.parentNode!.isHalfChecked) {
        node.isChecked = true;
        node.treeService!.refreshCheckState();
      }
    }
  }

  /**
   * 切换树形组件节点展开状态
   * @param event 树节点事件
   */
  _toggleMenuTreeNodeExpanded(event: NzFormatEmitEvent): void {
    event.node!.isExpanded = !event.node!.isExpanded;
  }

  /** 提交 */
  _submit(): void {
    this._submitLoading = true;
    const menuList = this._menuTreeList.toArray().reduce<any[]>((menuList, menuTree) => {
      return menuList
        .concat(menuTree.nzTreeService.halfCheckedNodeList
          .map(treeNode => treeNode.origin.source))
        .concat(menuTree.nzTreeService.checkedNodeList
          .filter(treeNode => treeNode.origin.source)
          .map(treeNode => treeNode.origin.source));
    }, []);
    const url = '@<%= getSeparatorCase(serviceName) %>/api/roles';
    const body = { ...this.role, menus: menuList };
    const submitSubscription = this._httpClient.put<any>(url, body)
      .pipe(tap(menu => {
        this._nzNotificationService.success('更新角色成功', null!);
        this._nzDrawerRef.close({
          success: true,
          updated: menu
        });
      }))
      .pipe(catchError((httpErrorResponse: HttpErrorResponse) => {
        this._nzNotificationService.error('更新角色失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
        return of(null);
      }))
      .subscribe(() => {
        this._submitLoading = false;
      });
    this._cancelSubmitFn = () => {
      submitSubscription.unsubscribe();
      this._cancelSubmitFn = () => { };
    };
  }

  /** 取消 */
  _cancel(): void {
    this._cancelSubmitFn();
    this._nzDrawerRef.close({
      success: false,
      updated: null
    });
  }

  /** 获取应用列表 */
  private _getApplicationList(): void {
    this._applicationLoading = true;
    const url = '@<%= getSeparatorCase(serviceName) %>/api/applications';
    this._httpClient.get<any[]>(url)
      .pipe(mergeMap(applicationList => {
        if (applicationList.length > 0) {
          return zip(...applicationList.map(application => {
            const url = '@<%= getSeparatorCase(serviceName) %>/api/menus';
            const params: any = { applicationCode: application.code };
            return this._httpClient.get<Application[]>(url, { params })
              .pipe(map(menuList => {
                return {
                  source: application,
                  menuTreeNodeOptionList: this._generateMenuTreeNodeOptionList(menuList),
                  menuCheckedKeys: this._filterMenuCheckedKeys(application)
                };
              }));
          }));
        } else {
          return of([]);
        }
      }))
      .pipe(catchError<Application[], Observable<Application[]>>((httpErrorResponse: HttpErrorResponse) => {
        this._nzNotificationService.error('获取应用列表失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
        return of([]);
      }))
      .subscribe(applicationList => {
        this._applicationList = applicationList.sort((application1, application2) => application1.source.sequence - application2.source.sequence);
        this._applicationLoading = false;
      });
  }

  /**
   * 生成菜单树形列表
   * @param menuList 菜单列表
   */
  private _generateMenuTreeNodeOptionList(menuList: any[]): NzTreeNodeOptions[] {
    const menuTreeNodeOptionList: NzTreeNodeOptions[] = [];
    const menuTreeNodeOptions: any = {};
    menuList.forEach(menu => {
      const menuTreeNodeOption: NzTreeNodeOptions = menuTreeNodeOptions[menu.id] = menuTreeNodeOptions[menu.id] || {};
      menuTreeNodeOption.source = menu;
      menuTreeNodeOption.key = menu.id.toString();
      menuTreeNodeOption.title = menu.name;
      menuTreeNodeOption.icon = menu.icon;
      menuTreeNodeOption.selectable = false;
      if (menu.type === '权限') {
        menuTreeNodeOption.isLeaf = true;
      } else {
        menuTreeNodeOption.children = [];
      }
      if (menu.type === '菜单') {
        menuTreeNodeOption.expanded = false;
        menuTreeNodeOption.children!.splice(0, 0, { key: menu.id + '-view', title: '查看', selectable: false, isLeaf: true });
      } else {
        menuTreeNodeOption.expanded = true;
      }
      if (menu.parentId) {
        const parentMenuTreeNodeOption: NzTreeNodeOptions = menuTreeNodeOptions[menu.parentId] = menuTreeNodeOptions[menu.parentId] || {};
        parentMenuTreeNodeOption.children!.push(menuTreeNodeOption);
      } else {
        menuTreeNodeOptionList.push(menuTreeNodeOption);
      }
    });
    return menuTreeNodeOptionList;
  }

  /** 筛选菜单选中键 */
  private _filterMenuCheckedKeys(application: any): string[] {
    const menuIds: any = {};
    const menuList: any[] = this.role.menus.filter((menu: any) => menu.applicationCode === application.code);
    menuList.forEach(menu => {
      if (menu.type === '菜单') {
        menuIds[menu.id + '-view'] = true;
      } else {
        menuIds[menu.id] = true;
      }
    });
    menuList.forEach(menu => {
      delete menuIds[menu.parentId];
    });
    return Object.keys(menuIds);
  }

}
