import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
import { NgCortexService, NcDictItem } from '@ng-cortex/core';
import { getEntityUpdate, isValid, validateForm, Validators } from '@ng-cortex/utils';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { NzTreeNodeOptions } from 'ng-zorro-antd/tree';
import { Observable, of } from 'rxjs';
import { catchError, distinctUntilChanged, map, mergeMap, tap } from 'rxjs/operators';

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

  /** 菜单 */
  @Input() menu: any = null;

  /** 菜单表单 */
  _menuForm!: FormGroup;
  /** 父级菜单必填状态 */
  _parentMenuRequired: boolean = false;
  /** 菜单列表 */
  _menuList: any[] = [];
  /** 菜单树形选项列表 */
  _menuTreeNodeOptionList: NzTreeNodeOptions[] = [];
  /** 菜单类型列表 */
  _menuTypeList: NcDictItem[] = [];
  /** 菜单类型选项列表 */
  _menuTypeOptionList: NcDictItem[] = [];

  /** 菜单额外参数控制器 */
  get _menuControlOfExtras(): FormArray { return this._menuForm.controls.extras as FormArray }

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

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

  /** 初始化后 */
  ngOnInit(): void {
    this._menuForm = this._formBuilder.group({
      applicationCode: [null],
      name: [null, [Validators.required, Validators.maxLength(20)]],
      type: [null, [Validators.required, Validators.maxLength(20)]],
      icon: [null, [Validators.maxLength(200)]],
      authorityIdentifier: [null, [Validators.maxLength(100)]],
      sequence: [null],
      url: [null, [Validators.required, Validators.maxLength(200)]],
      src: [null, [Validators.required, Validators.maxLength(200)]],
      visible: [null],
      extras: this._formBuilder.array([], [Validators.maxLength(200)]),
      parentId: [null]
    });

    const menuFormControls = this._menuForm.controls;
    menuFormControls.parentId.valueChanges
      .pipe(distinctUntilChanged())
      .subscribe(parentId => {
        this._generateMenuTypeOptionList(parentId);
      });
    menuFormControls.type.valueChanges
      .pipe(distinctUntilChanged())
      .subscribe(type => {
        if (/^(文件夹|菜单|链接)$/.test(type)) {
          menuFormControls.icon.enable();
          menuFormControls.sequence.enable();
          menuFormControls.visible.enable();
          menuFormControls.visible.reset(true);
        } else {
          menuFormControls.icon.disable();
          menuFormControls.icon.reset();
          menuFormControls.sequence.disable();
          menuFormControls.sequence.reset();
          menuFormControls.visible.disable();
          menuFormControls.visible.reset(false);
        }
        if (/^(菜单|权限)$/.test(type)) {
          menuFormControls.authorityIdentifier.enable();
        } else {
          menuFormControls.authorityIdentifier.disable();
          menuFormControls.authorityIdentifier.reset();
        }
        if (/^(菜单|链接)$/.test(type)) {
          menuFormControls.url.enable();
        } else {
          menuFormControls.url.disable();
          menuFormControls.url.reset();
        }
        if (/^菜单$/.test(type)) {
          menuFormControls.src.enable();
          menuFormControls.extras.enable();
        } else {
          menuFormControls.src.disable();
          menuFormControls.src.reset();
          menuFormControls.extras.disable();
          this._menuControlOfExtras.clear();
        }
      });

    const menuFormPatch = { ...this.menu };
    if (menuFormPatch.id) {
      menuFormControls.type.disable();
      if (menuFormPatch.type === '权限') {
        this._parentMenuRequired = true;
        menuFormControls.parentId.setValidators(Validators.required);
      }
    }
    if (menuFormPatch.parentId) {
      menuFormPatch.parentId = menuFormPatch.parentId.toString();
    }
    if (menuFormPatch.extras) {
      try {
        const extras = JSON.parse(menuFormPatch.extras);
        for (const key in extras) {
          this._addExtra(key, extras[key]);
        }
      } catch (error) { }
    }
    delete menuFormPatch.extras;
    this._menuForm.patchValue(menuFormPatch);

    this._getMenuList();

    this._ngCortexService.getDictItemList('menuType')
      .pipe(catchError((httpErrorResponse: HttpErrorResponse) => {
        this._nzNotificationService.error('获取菜单类型列表失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
        return of([]);
      }))
      .subscribe(menuTypeList => {
        this._menuTypeList = menuTypeList;
        this._generateMenuTypeOptionList(this._menuForm.getRawValue().parentId);
      });
  }

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

  /** 提交 */
  submit(): Observable<{ success: boolean; updated: any }> {
    return new Observable(subscriber => {
      const submitResult = {
        success: false,
        updated: null
      };
      const submitSubscription = validateForm(this._menuForm)
        .pipe(mergeMap(valid => {
          if (valid) {
            const menuUpdate = this._getMenuUpdate();
            if (menuUpdate) {
              return this._submitMenu(menuUpdate)
                .pipe(tap(menuUpdated => {
                  submitResult.success = true;
                  submitResult.updated = menuUpdated;
                }));
            } else {
              submitResult.success = true;
            }
          }
          return of(null);
        }))
        .pipe(catchError(() => of(null)))
        .pipe(map(() => submitResult))
        .subscribe(subscriber);
      this._cancelSubmitFn = () => {
        submitSubscription.unsubscribe();
      };
    });
  }

  /**
   * 添加额外参数
   * @param key 键
   * @param value 值
   */
  _addExtra(key?: string, value?: string): void {
    this._menuControlOfExtras.push(this._formBuilder.group({
      key: [key || '', [Validators.required]],
      value: [isValid(value) ? value : '', [Validators.required]]
    }));
  }

  /**
   * 移除额外参数
   * @param index 索引
   */
  _removeExtra(index: number): void {
    this._menuControlOfExtras.removeAt(index);
  }

  /** 获取菜单更新 */
  private _getMenuUpdate(): any {
    const update = { ...this._menuForm.getRawValue() };
    if (update.parentId) {
      update.parentId = Number(update.parentId);
    }
    if (update.extras.length > 0) {
      const extras: any = {};
      update.extras.forEach((extra: any) => {
        extras[extra.key] = extra.value;
      });
      update.extras = JSON.stringify(extras);
    } else {
      update.extras = null;
    }
    for (const key in update) {
      if (!isValid(update[key])) {
        update[key] = null;
      }
    }
    return getEntityUpdate(update, this.menu) && { ...this.menu, ...update };
  }

  /**
   * 提交菜单
   * @param update 更新
   */
  private _submitMenu(update: any): Observable<any> {
    const url = '@<%= getSeparatorCase(serviceName) %>/api/menus';
    const body = update;
    if (body.id) {
      return this._httpClient.put(url, body).pipe(tap(
        () => {
          this._nzNotificationService.success('更新菜单成功', null!);
        },
        (httpErrorResponse: HttpErrorResponse) => {
          this._nzNotificationService.error('更新菜单失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
        }));
    } else {
      return this._httpClient.post(url, body).pipe(tap(
        () => {
          this._nzNotificationService.success('新增菜单成功', null!);
        },
        (httpErrorResponse: HttpErrorResponse) => {
          this._nzNotificationService.error('新增菜单失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
        }));
    }
  }

  /** 获取菜单列表 */
  private _getMenuList(): void {
    const { applicationCode } = this._menuForm.getRawValue();
    const url = '@<%= getSeparatorCase(serviceName) %>/api/menus';
    const params: any = { applicationCode };
    this._httpClient.get<any[]>(url, { params })
      .pipe(catchError((httpErrorResponse: HttpErrorResponse) => {
        this._nzNotificationService.success('获取菜单列表失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
        return of([]);
      }))
      .subscribe(menuList => {
        this._menuList = menuList.sort((menu1, menu2) => menu1.sequence - menu2.sequence);
        this._generateMenuTreeOptionList();
        this._generateMenuTypeOptionList(this._menuForm.getRawValue().parentId);
      });
  }

  /** 生成菜单树形选项列表 */
  private _generateMenuTreeOptionList(): void {
    const menuTreeNodeOptionList: NzTreeNodeOptions[] = [];
    const menuTreeNodeOptions: any = {};
    const { type } = this._menuForm.getRawValue();
    let parentMenuFilterFn;
    if (this.menu && this.menu.id) {
      switch (type) {
        case '权限':
          parentMenuFilterFn = (menu: any) => /^(文件夹|菜单)$/.test(menu.type);
          break;
        case '文件夹':
          parentMenuFilterFn = (menu: any) => /^文件夹$/.test(menu.type) && menu.id !== this.menu.id;
          break;
        default:
          parentMenuFilterFn = (menu: any) => /^文件夹$/.test(menu.type);
      }
    } else {
      parentMenuFilterFn = (menu: any) => /^(文件夹|菜单)$/.test(menu.type);
    }
    this._menuList
      .filter(parentMenuFilterFn)
      .forEach(menu => {
        const menuTreeNodeOption: NzTreeNodeOptions = menuTreeNodeOptions[menu.id] = menuTreeNodeOptions[menu.id] || {};
        menuTreeNodeOption.key = menu.id.toString();
        menuTreeNodeOption.title = menu.name;
        if (menu.type === '文件夹' && type === '权限') {
          menuTreeNodeOption.disabled = true;
        }
        if (!menuTreeNodeOption.children) {
          menuTreeNodeOption.isLeaf = true;
        }
        if (menu.parentId) {
          const parentMenuTreeNodeOption: NzTreeNodeOptions = menuTreeNodeOptions[menu.parentId] = menuTreeNodeOptions[menu.parentId] || {};
          if (!parentMenuTreeNodeOption.children) {
            delete parentMenuTreeNodeOption.isLeaf;
            parentMenuTreeNodeOption.children = [];
          }
          parentMenuTreeNodeOption.children.push(menuTreeNodeOption);
        } else {
          menuTreeNodeOptionList.push(menuTreeNodeOption);
        }
      });
    this._menuTreeNodeOptionList = menuTreeNodeOptionList;
  }

  /**
   * 生成菜单类型选项列表
   * @param parentId 父级id
   */
  private _generateMenuTypeOptionList(parentId: any): void {
    const parentMenu = this._menuList.find(menu => menu.id.toString() === parentId) || { type: '文件夹' };
    let defaultType;
    switch (parentMenu.type) {
      case '文件夹':
        this._menuTypeOptionList = this._menuTypeList.filter(menuType => /^(文件夹|菜单|链接)$/.test(menuType.value));
        defaultType = '菜单';
        break;
      case '菜单':
        this._menuTypeOptionList = this._menuTypeList.filter(menuType => /^权限$/.test(menuType.value));
        defaultType = '权限';
        break;
      default:
        this._menuTypeOptionList = [];
        defaultType = null;
    }
    const menuFormControls = this._menuForm.controls;
    if (menuFormControls.type.enabled) {
      const { type } = this._menuForm.getRawValue();
      const menuTypeOption = this._menuTypeOptionList.find(menuTypeOption => menuTypeOption.value === type);
      if (!menuTypeOption) {
        menuFormControls.type.patchValue(defaultType);
      }
    }
  }

}
