import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { AfterViewInit, Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { NcActionStatus, NcOptionResource, NcTableField, NgCortexService } from '@ng-cortex/core';
import { getErrorMessage, isValid, validateForm, Validators } from '@ng-cortex/utils';
import { NzModalService } from 'ng-zorro-antd/modal';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { Observable, of } from 'rxjs';
import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators';
import { NcMenuEditorComponent } from '../nc-menu-editor/nc-menu-editor.component';

/** 树形节点 */
interface TreeNode {
  source: any;
  level: number;
  expand?: boolean;
  children?: TreeNode[];
}

/** 菜单管理页面 */
@Component({
  selector: 'nc-menu',
  templateUrl: './nc-menu.page.html',
  styleUrls: ['./nc-menu.page.less']
})
export class NcMenuPage implements OnInit, AfterViewInit {

  /** 标题模板 */
  @ViewChild('titleTemplate') _titleTemplate!: TemplateRef<{ $implicit: string; source: any; }>;
  /** 数字模板 */
  @ViewChild('numberTemplate') _numberTemplate!: TemplateRef<{ $implicit: number; }>;
  /** 时间模板 */
  @ViewChild('timeTemplate') _timeTemplate!: TemplateRef<{ $implicit: string; }>;
  /** 是否显示主体模板 */
  @ViewChild('visibleBodyTemplate') _visibleBodyTemplate!: TemplateRef<{ $implicit: boolean; }>;

  /** 菜单列表 */
  _menuList: any[] = [];
  /** 菜单树形节点列表 */
  _menuTreeNodeList: TreeNode[] = [];
  /** 菜单额外属性 */
  _menuExtras: { [id: string]: any; } = {};
  /** 菜单搜索表单 */
  _menuSearchForm!: FormGroup;
  /** 菜单加载状态 */
  _menuLoading: boolean = false;
  /** 菜单全部选中状态 */
  _menuAllChecked: boolean = false;
  /** 菜单模糊选中状态 */
  _menuIndeterminate: boolean = false;
  /** 菜单字段列表 */
  _menuFieldList: NcTableField[] = [
    { key: 'name', name: '菜单名称', visible: true, width: '200px' },
    { key: 'type', name: '菜单类型', visible: true, width: '200px' },
    { key: 'visible', name: '是否显示', visible: true, width: '200px' },
    { key: 'sequence', name: '顺序号', visible: true, align: 'right', width: '80px' },
    { key: 'url', name: '链接', visible: true, width: '200px' },
    { key: 'authorityIdentifier', name: '权限标识', visible: true, width: '200px' },
    { key: 'src', name: '模块地址', width: '560px' },
    { key: 'extras', name: '额外配置', width: '200px' },
    { key: 'createdBy', name: '创建人', width: '120px' },
    { key: 'createdTime', name: '创建时间', width: '160px' },
    { key: 'lastModifiedBy', name: '最后更新人', width: '120px' },
    { key: 'lastModifiedTime', name: '最后更新时间', width: '160px' }
  ];
  /** 新增菜单状态 */
  _createMenuStatus: NcActionStatus = false;
  /** 应用选项资源 */
  _applicationOptionResource!: NcOptionResource;

  /** 取消获取菜单列表 */
  private _cancelGetMenuListFn = () => { };

  constructor(
    private _formBuilder: FormBuilder,
    private _httpClient: HttpClient,
    private _ngCortexService: NgCortexService,
    private _nzModalService: NzModalService,
    private _nzNotificationService: NzNotificationService) { }

  /** 初始化后 */
  ngOnInit(): void {

    // 初始化菜单字段列表
    this._menuFieldList.forEach((field, index) => {
      field.defaultSequence = index + 1;
      field.defaultVisible = field.visible;
      field.defaultWidth = field.width;
    });

    // 构建菜单搜索表单
    this._menuSearchForm = this._formBuilder.group({
      applicationCode: [null, [Validators.required]]
    });
    const { applicationCode } = this._menuSearchForm.controls;

    // 定义菜单搜索表单规则
    applicationCode.valueChanges
      .pipe(distinctUntilChanged())
      .subscribe(() => this._updateMenuActionStatus());

    // 初始化较验菜单搜索表单
    validateForm(this._menuSearchForm, true).subscribe(() => {
      this._updateMenuActionStatus();
    });

    // 获取应用选项资源
    const applicationOptionUrl = 'source::@' + this._ngCortexService.coreService + '/api/applications';
    this._applicationOptionResource = this._ngCortexService.getOptionResource(applicationOptionUrl, {
      tap: (applicationList: any[]) => {
        if (applicationList.length > 0) {
          this._menuSearchForm.patchValue({ applicationCode: applicationList[0].code });
          this._queryMenuList();
        }
      },
      map: (applicationList: any[]) => applicationList.sort((application1, application2) => application1.sequence - application2.sequence),
      error: error => this._nzNotificationService.error('获取应用列表失败', getErrorMessage(error))
    });
  }

  /** 初始次渲染后 */
  ngAfterViewInit(): void {
    this._menuFieldList.forEach(field => {
      if (/^(name)$/.test(field.key)) {
        field.bodyTemplate = this._titleTemplate;
      }
      if (/^(sequence)$/.test(field.key)) {
        field.bodyTemplate = this._numberTemplate;
      }
      if (/^(createdTime|lastModifiedTime)$/.test(field.key)) {
        field.bodyTemplate = this._timeTemplate;
      }
      if (/^(visible)$/.test(field.key)) {
        field.bodyTemplate = this._visibleBodyTemplate;
      }
    });
  }

  /** 查询菜单列表 */
  _queryMenuList(): void {
    validateForm(this._menuSearchForm).subscribe(() => this._getMenuList().subscribe());
  }

  /** 重新查询菜单列表 */
  _requeryMenuList(): void {
    this._queryMenuList();
  }

  /** 重置菜单搜索表单 */
  _resetMenuSearchForm(): void {
    this._menuSearchForm.reset();
    this._queryMenuList();
  }

  /**
   * 拖拽菜单字段列表
   * @param event 拖拽事件
   */
  _dropMenuFieldList(event: CdkDragDrop<string[]>): void {
    moveItemInArray(this._menuFieldList, event.previousIndex, event.currentIndex);
  }

  /** 重置菜单字段列表 */
  _resetMenuFieldList(): void {
    this._menuFieldList = this._menuFieldList
      .map(field => {
        field.visible = field.defaultVisible!;
        field.width = field.defaultWidth;
        return field;
      })
      .sort((fieldA, fieldB) => fieldA.defaultSequence! - fieldB.defaultSequence!);
  }

  /**
   * 选中所有菜单列表
   * @param checked 选中状态
   */
  _checkAllMenuList(checked: boolean): void {
    for (const id in this._menuExtras) {
      this._menuExtras[id].checked = checked;
    }
    this._menuAllChecked = checked;
    this._menuIndeterminate = false;
    this._updateMenuActionStatus();
  }

  /** 更新菜单选中状态 */
  _updateMenuCheckStatus(): void {
    const checkedMenuList = this._menuList.filter(menu => this._menuExtras[menu.id].checked);
    switch (checkedMenuList.length) {
      case 0:
        this._menuAllChecked = false;
        this._menuIndeterminate = false;
        break;
      case this._menuList.length:
        this._menuAllChecked = true;
        this._menuIndeterminate = false;
        break;
      default:
        this._menuAllChecked = false;
        this._menuIndeterminate = true;
    }
    this._updateMenuActionStatus();
  }

  /** 新增菜单 */
  _createMenu(): void {
    const menuList = this._menuList.filter(menu => this._menuExtras[menu.id].checked);
    const { applicationCode } = this._menuSearchForm.getRawValue();
    this._nzModalService.create({
      nzTitle: '新增菜单',
      nzContent: NcMenuEditorComponent,
      nzComponentParams: {
        menu: {
          applicationCode: applicationCode,
          parentId: menuList.length === 1 ? menuList[0].id : null
        }
      },
      nzWidth: 520,
      nzOnOk: menuEditor => {
        return menuEditor.submit()
          .pipe(map(({ success, updated }) => {
            if (updated) {
              this._queryMenuList();
            }
            return success;
          }))
          .toPromise();
      }
    });
  }

  /**
   * 编辑菜单
   * @param menu 菜单
   */
  _updateMenu(menu: any): void {
    this._nzModalService.create({
      nzTitle: '编辑菜单',
      nzContent: NcMenuEditorComponent,
      nzComponentParams: {
        menu: menu
      },
      nzWidth: 520,
      nzOnOk: menuEditor => {
        return menuEditor.submit()
          .pipe(map(({ success, updated }) => {
            if (updated) {
              this._queryMenuList();
            }
            return success;
          }))
          .toPromise();
      }
    });
  }

  /**
   * 删除菜单
   * @param menu 菜单
   */
  _deleteMenu(menu: any): void {
    let cancelDeleteMenuFn = () => { };
    this._nzModalService.confirm({
      nzTitle: '删除确认',
      nzContent: '是否确认删除菜单 “' + menu.name + '”',
      nzOnOk: () => {
        return new Observable(subscriber => {
          const url = '@' + this._ngCortexService.coreService + '/api/menus/' + menu.id;
          const deleteMenuSubscription = this._httpClient.delete(url, { responseType: 'text' })
            .pipe(map(() => {
              this._queryMenuList();
              return true;
            }))
            .pipe(catchError((httpErrorResponse: HttpErrorResponse) => {
              this._nzNotificationService.error('删除菜单失败', getErrorMessage(httpErrorResponse));
              return of(false);
            }))
            .subscribe(subscriber);
          cancelDeleteMenuFn = () => {
            deleteMenuSubscription.unsubscribe();
          };
        })
          .toPromise();
      },
      nzOnCancel: () => {
        cancelDeleteMenuFn();
      }
    });
  }

  /** 展开菜单列表 */
  _expandMenuList(): void {
    const eachMenu = (menuTreeList: TreeNode[]) => {
      menuTreeList
        .filter(menuNode => menuNode.children)
        .forEach(menuNode => {
          menuNode.expand = true;
          eachMenu(menuNode.children!);
        });
    };
    eachMenu(this._menuTreeNodeList);
  }

  /** 折叠菜单列表 */
  _collapseMenuList(): void {
    const eachMenu = (menuTreeList: TreeNode[]) => {
      menuTreeList
        .filter(menuNode => menuNode.children)
        .forEach(menuNode => {
          menuNode.expand = false;
          eachMenu(menuNode.children!);
        });
    };
    eachMenu(this._menuTreeNodeList);
  }

  /** 获取菜单列表 */
  private _getMenuList(): Observable<HttpErrorResponse | Error | void> {
    this._cancelGetMenuListFn();
    return new Observable(subscriber => {
      if (this._menuSearchForm.valid) {
        this._menuLoading = true;
        const completeGetMenuListFn = () => {
          this._cancelGetMenuListFn = () => { };
          this._menuLoading = false;
        };
        const url = '@' + this._ngCortexService.coreService + '/api/menus';
        const params: any = { ...this._menuSearchForm.getRawValue() };
        for (const key in params) {
          if (!isValid(params[key])) {
            delete params[key];
          }
        }
        const getMenuListSubscription = this._httpClient.get<any[]>(url, { params })
          .pipe(tap(() => subscriber.next(undefined)))
          .pipe(catchError((httpErrorResponse: HttpErrorResponse) => {
            this._nzNotificationService.error('获取菜单列表失败', getErrorMessage(httpErrorResponse));
            subscriber.next(httpErrorResponse);
            return of([]);
          }))
          .subscribe(menuList => {
            completeGetMenuListFn();
            const menuExtras: { [id: string]: any; } = {};
            menuList
              .sort((menu1, menu2) => menu1.sequence - menu2.sequence)
              .forEach(menu => {
                menuExtras[menu.id] = { checked: false };
              });
            this._menuList = menuList;
            this._menuExtras = menuExtras;
            this._menuAllChecked = false;
            this._menuIndeterminate = false;
            this._generateMenuTreeNodeList();
            this._updateMenuActionStatus();
            subscriber.complete();
          });
        this._cancelGetMenuListFn = () => {
          getMenuListSubscription.unsubscribe();
          completeGetMenuListFn();
          subscriber.next(new Error('取消获取菜单列表'));
          subscriber.complete();
        };
      } else {
        subscriber.next(new Error('筛选菜单不合法'));
        subscriber.complete();
      }
    });
  }

  /** 生成菜单树形节点列表 */
  private _generateMenuTreeNodeList(): void {
    const menuTreeNodeList: TreeNode[] = [];
    const menuTreeNodes: { [id: string]: TreeNode; } = {};
    this._menuList.forEach(menu => {
      const menuTreeNode = menuTreeNodes[menu.id] = menuTreeNodes[menu.id] || {};
      menuTreeNode.source = menu;
      if (menu.parentId) {
        const parentMenuTreeNode = menuTreeNodes[menu.parentId] = menuTreeNodes[menu.parentId] || {};
        if (!parentMenuTreeNode.children) {
          parentMenuTreeNode.expand = false;
          parentMenuTreeNode.children = [];
        }
        parentMenuTreeNode.children.push(menuTreeNode);
      } else {
        menuTreeNodeList.push(menuTreeNode);
      }
    });
    const markLevelFn = (menuTreeList: TreeNode[], level?: number) => {
      level = level || 0;
      menuTreeList.forEach(menuTreeNode => {
        if (menuTreeNode.children) {
          markLevelFn(menuTreeNode.children, level! + 1);
        }
        menuTreeNode.level = level!;
      });
    };
    markLevelFn(menuTreeNodeList);
    this._menuTreeNodeList = menuTreeNodeList;
  }

  /** 更新菜单操作状态 */
  private _updateMenuActionStatus(): void {
    const { applicationCode } = this._menuSearchForm.getRawValue();
    if (applicationCode) {
      this._createMenuStatus = true;
    } else {
      this._createMenuStatus = { disabled: true, hint: '未选中应用' };
    }
  }

}
