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 { NcTableField, NgCortexService } from '@ng-cortex/core';
import { getErrorMessage, isValid, validateForm } 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, map, tap } from 'rxjs/operators';
import { NcOrganizationEditorComponent } from '../nc-organization-editor/nc-organization-editor.component';

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

/** 机构管理页面 */
@Component({
  selector: 'nc-organization',
  templateUrl: './nc-organization.page.html',
  styleUrls: ['./nc-organization.page.less']
})
export class NcOrganizationPage implements OnInit, AfterViewInit {

  /** 标题模板 */
  @ViewChild('titleTemplate') _titleTemplate!: TemplateRef<{ $implicit: string; source: any; }>;
  /** 数字模板 */
  @ViewChild('numberTemplate') _numberTemplate!: TemplateRef<{ $implicit: number; }>;
  /** 时间模板 */
  @ViewChild('timeTemplate') _timeTemplate!: TemplateRef<{ $implicit: string; }>;

  /** 机构列表 */
  _organizationList: any[] = [];
  /** 机构树形节点列表 */
  _organizationTreeNodeList: TreeNode[] = [];
  /** 机构额外属性 */
  _organizationExtras: { [id: string]: any; } = {};
  /** 机构搜索表单 */
  _organizationSearchForm!: FormGroup;
  /** 机构加载状态 */
  _organizationLoading: boolean = false;
  /** 机构全部选中状态 */
  _organizationAllChecked: boolean = false;
  /** 机构模糊选中状态 */
  _organizationIndeterminate: boolean = false;
  /** 机构字段列表 */
  _organizationFieldList: NcTableField[] = [
    { key: 'name', name: '机构名称', visible: true, width: '300px' },
    { key: 'type', name: '机构类型', visible: true, width: '200px' },
    { key: 'sequence', name: '顺序号', visible: true, align: 'right', width: '80px' },
    { key: 'createdBy', name: '创建人', width: '120px' },
    { key: 'createdTime', name: '创建时间', width: '160px' },
    { key: 'lastModifiedBy', name: '最后更新人', width: '120px' },
    { key: 'lastModifiedTime', name: '最后更新时间', width: '160px' }
  ];

  /** 取消获取机构列表 */
  private _cancelGetOrganizationListFn = () => { };

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

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

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

    // 构建机构搜索表单
    this._organizationSearchForm = this._formBuilder.group({
      keywords: [null]
    });

    // 初始化较验机构搜索表单
    validateForm(this._organizationSearchForm, true).subscribe(() => this._getOrganizationList().subscribe());
  }

  /** 初始次渲染后 */
  ngAfterViewInit(): void {
    this._organizationFieldList.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;
      }
    });
  }

  /** 查询机构列表 */
  _queryOrganizationList(): void {
    validateForm(this._organizationSearchForm).subscribe(() => this._getOrganizationList().subscribe());
  }

  /** 重新查询机构列表 */
  _requeryOrganizationList(): void {
    this._queryOrganizationList();
  }

  /** 重置机构搜索表单 */
  _resetOrganizationSearchForm(): void {
    this._organizationSearchForm.reset();
    this._queryOrganizationList();
  }

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

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

  /**
   * 选中所有机构列表
   * @param checked 选中状态
   */
  _checkAllOrganizationList(checked: boolean): void {
    for (const id in this._organizationExtras) {
      this._organizationExtras[id].checked = checked;
    }
    this._organizationAllChecked = checked;
    this._organizationIndeterminate = false;
  }

  /** 更新机构选中状态 */
  _updateOrganizationCheckStatus(): void {
    const checkedOrganizationList = this._organizationList.filter(organization => this._organizationExtras[organization.id].checked);
    switch (checkedOrganizationList.length) {
      case 0:
        this._organizationAllChecked = false;
        this._organizationIndeterminate = false;
        break;
      case this._organizationList.length:
        this._organizationAllChecked = true;
        this._organizationIndeterminate = false;
        break;
      default:
        this._organizationAllChecked = false;
        this._organizationIndeterminate = true;
    }
  }

  /** 新增机构 */
  _createOrganization(): void {
    const organizationList = this._organizationList.filter(organization => this._organizationExtras[organization.id].checked);
    this._nzModalService.create({
      nzTitle: '新增机构',
      nzContent: NcOrganizationEditorComponent,
      nzComponentParams: {
        organization: { parentId: organizationList.length === 1 ? organizationList[0].id : null }
      },
      nzWidth: 520,
      nzOnOk: organizationEditor => {
        return organizationEditor.submit()
          .pipe(map(({ success, updated }) => {
            if (updated) {
              this._queryOrganizationList();
            }
            return success;
          }))
          .toPromise();
      }
    });
  }

  /**
   * 编辑机构
   * @param organization 机构
   */
  _updateOrganization(organization: any): void {
    this._nzModalService.create({
      nzTitle: '编辑机构',
      nzContent: NcOrganizationEditorComponent,
      nzComponentParams: {
        organization: organization
      },
      nzWidth: 520,
      nzOnOk: organizationEditor => {
        return organizationEditor.submit()
          .pipe(map(({ success, updated }) => {
            if (updated) {
              const targetOrganization = updated;
              this._organizationList = this._organizationList.map(organization => organization.id === targetOrganization.id ? targetOrganization : organization);
              this._generateOrganizationTreeNodeList();
            }
            return success;
          }))
          .toPromise();
      }
    });
  }

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

  /** 展开机构列表 */
  _expandOrganizationList(): void {
    const eachOrganization = (organizationTreeList: TreeNode[]) => {
      organizationTreeList
        .filter(organizationNode => organizationNode.children)
        .forEach(organizationNode => {
          organizationNode.expand = true;
          eachOrganization(organizationNode.children!);
        });
    };
    eachOrganization(this._organizationTreeNodeList);
  }

  /** 折叠机构列表 */
  _collapseOrganizationList(): void {
    const eachOrganization = (organizationTreeList: TreeNode[]) => {
      organizationTreeList
        .filter(organizationNode => organizationNode.children)
        .forEach(organizationNode => {
          organizationNode.expand = false;
          eachOrganization(organizationNode.children!);
        });
    };
    eachOrganization(this._organizationTreeNodeList);
  }

  /** 获取机构列表 */
  private _getOrganizationList(): Observable<HttpErrorResponse | Error | void> {
    this._cancelGetOrganizationListFn();
    return new Observable(subscriber => {
      if (this._organizationSearchForm.valid) {
        this._organizationLoading = true;
        const completeGetOrganizationListFn = () => {
          this._cancelGetOrganizationListFn = () => { };
          this._organizationLoading = false;
        };
        const url = '@' + this._ngCortexService.coreService + '/api/organizations';
        const params: any = { ...this._organizationSearchForm.getRawValue() };
        for (const key in params) {
          if (!isValid(params[key])) {
            delete params[key];
          }
        }
        const getOrganizationListSubscription = 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(organizationList => {
            completeGetOrganizationListFn();
            const organizationExtras: { [id: string]: any; } = {};
            organizationList
              .sort((organization1, organization2) => organization1.sequence - organization2.sequence)
              .forEach(organization => {
                organizationExtras[organization.id] = { checked: false };
              });
            this._organizationList = organizationList;
            this._organizationExtras = organizationExtras;
            this._organizationAllChecked = false;
            this._organizationIndeterminate = false;
            this._generateOrganizationTreeNodeList();
            subscriber.complete();
          });
        this._cancelGetOrganizationListFn = () => {
          getOrganizationListSubscription.unsubscribe();
          completeGetOrganizationListFn();
          subscriber.next(new Error('取消获取机构列表'));
          subscriber.complete();
        };
      } else {
        subscriber.next(new Error('筛选机构不合法'));
        subscriber.complete();
      }
    });
  }

  /** 生成机构树形节点列表 */
  private _generateOrganizationTreeNodeList(): void {
    const organizationTreeNodeList: TreeNode[] = [];
    const organizationTreeNodes: { [id: string]: TreeNode; } = {};
    this._organizationList
      .slice()
      .sort((organization1, organization2) => organization1.sequence - organization2.sequence)
      .forEach(organization => {
        const organizationTreeNode = organizationTreeNodes[organization.id] = organizationTreeNodes[organization.id] || {};
        organizationTreeNode.source = organization;
        if (organization.parentId) {
          const parentOrganizationTreeNode = organizationTreeNodes[organization.parentId] = organizationTreeNodes[organization.parentId] || {};
          if (!parentOrganizationTreeNode.children) {
            parentOrganizationTreeNode.expand = false;
            parentOrganizationTreeNode.children = [];
          }
          parentOrganizationTreeNode.children.push(organizationTreeNode);
        } else {
          organizationTreeNodeList.push(organizationTreeNode);
        }
      });
    const markLevelFn = (organizationTreeList: TreeNode[], level?: number) => {
      level = level || 0;
      organizationTreeList.forEach(organizationTreeNode => {
        if (organizationTreeNode.children) {
          markLevelFn(organizationTreeNode.children, level! + 1);
        }
        organizationTreeNode.level = level!;
      });
    };
    markLevelFn(organizationTreeNodeList);
    this._organizationTreeNodeList = organizationTreeNodeList;
  }

}
