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 { 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 } from 'rxjs/operators';
import { NcMenuEditorComponent } from '../nc-menu-editor/nc-menu-editor.component';

/** 字段接口 */
interface Field {
  /** 名称 */
  name: string;
  /** 描述 */
  description?: string;
  /** 可见状态 */
  visible?: boolean;
  /** 对齐 */
  align?: 'left' | 'right' | 'center';
  /** 宽度 */
  width?: string;
  /** 排序键 */
  sortKey?: string;
  /** 排序状态 */
  sortOrder?: string | null;
  /** 默认排序 */
  defaultSequence?: number;
  /** 默认可见状态 */
  defaultVisible?: boolean;
  /** 默认宽度 */
  defaultWidth?: string;
  /** 标题模板 */
  titleTemplate?: TemplateRef<any>;
  /** 内容模板 */
  contentTemplate?: TemplateRef<{ $implicit?: any; source?: any; extra?: any; }>;
}

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

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

  /** 标题模板 */
  @ViewChild('titleTemplate') _titleTemplate!: TemplateRef<{ $implicit: string; source: any; }>;
  /** 数字模板 */
  @ViewChild('numberContentTemplate') _numberContentTemplate!: TemplateRef<{ $implicit: number; }>;
  /** 显示模板 */
  @ViewChild('visibleTemplate') _visibleTemplate!: TemplateRef<{ $implicit: boolean; }>;
  /** 时间模板 */
  @ViewChild('timeTemplate') _timeTemplate!: TemplateRef<{ $implicit: string; }>;

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

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

  constructor(
    private _formBuilder: FormBuilder,
    private _httpClient: HttpClient,
    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._updateMenuHandleHints());
    validateForm(this._menuSearchForm, true).subscribe(() => {
      this._updateMenuHandleHints();
      this._getApplicationList();
    });
  }

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

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

  /** 重新查询菜单列表 */
  _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._updateMenuHandleHints();
  }

  /** 更新菜单选中状态 */
  _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._updateMenuHandleHints();
  }

  /** 新增菜单 */
  _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 = '@<%= getSeparatorCase(serviceName) %>/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('删除菜单失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
              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 _getApplicationList(): void {
    this._applicationLoading = true;
    const url = '@<%= getSeparatorCase(serviceName) %>/api/applications';
    this._httpClient.get<any[]>(url)
      .pipe(catchError((httpErrorResponse: HttpErrorResponse) => {
        this._nzNotificationService.error('获取应用列表失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
        return of([]);
      }))
      .subscribe(applicationList => {
        this._applicationList = applicationList.sort((application1, application2) => application1.sequence - application2.sequence);
        this._applicationLoading = false;
        if (this._applicationList.length > 0) {
          this._menuSearchForm.patchValue({ applicationCode: this._applicationList[0].code });
          this._queryMenuList();
        }
      });
  }

  /** 获取菜单列表 */
  private _getMenuList(): void {
    this._cancelGetMenuListFn();
    if (this._menuSearchForm.valid) {
      this._menuLoading = true;
      const completeGetMenuListFn = () => {
        this._cancelGetMenuListFn = () => { };
        this._menuLoading = false;
      };
      const url = '@<%= getSeparatorCase(serviceName) %>/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(catchError((httpErrorResponse: HttpErrorResponse) => {
          this._nzNotificationService.error('获取菜单列表失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
          return of([]);
        }))
        .subscribe(menuList => {
          completeGetMenuListFn();
          const menuExtras: 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._updateMenuHandleHints();
        });
      this._cancelGetMenuListFn = () => {
        getMenuListSubscription.unsubscribe();
        completeGetMenuListFn();
      };
    } else {
      this._menuList = [];
      this._menuExtras = {};
      this._menuAllChecked = false;
      this._menuIndeterminate = false;
      this._menuTreeNodeList = [];
      this._updateMenuHandleHints();
    }
  }

  /** 生成菜单树形节点列表 */
  private _generateMenuTreeNodeList(): void {
    const menuTreeNodeList: TreeNode[] = [];
    const menuTreeNodes: any = {};
    this._menuList.forEach(menu => {
      const menuTreeNode: TreeNode = menuTreeNodes[menu.id] = menuTreeNodes[menu.id] || {};
      menuTreeNode.source = menu;
      if (menu.parentId) {
        const parentMenuTreeNode: TreeNode = 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 _updateMenuHandleHints(): void {
    const { applicationCode } = this._menuSearchForm.getRawValue();
    if (applicationCode) {
      this._createMenuHint = null;
    } else {
      this._createMenuHint = '未选中应用';
    }
  }

}
