import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { NcOptionResource, NgCortexService } from '@ng-cortex/core';
import { getEntityUpdate, getErrorMessage, 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, map, mergeMap, tap } from 'rxjs/operators';

/** 用户编辑器组件 */
@Component({
  selector: 'nc-user-editor',
  templateUrl: './nc-user-editor.component.html',
  styleUrls: ['./nc-user-editor.component.less']
})
export class NcUserEditorComponent implements OnInit, OnDestroy {

  /** 用户 */
  @Input() user: any = null;

  /** 用户表单 */
  _userForm!: FormGroup;
  /** 密码可见状态 */
  _passwordVisible: boolean = false;
  /** 机构选项资源 */
  _organizationOptionResource!: NcOptionResource;
  /** 角色选项资源 */
  _roleOptionResource!: NcOptionResource;

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

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

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

    // 构建用户表单
    this._userForm = this._formBuilder.group({
      companyId: [null],
      organizationId: [null, [Validators.required]],
      account: [null, [Validators.required, Validators.maxLength(30)]],
      password: [null, [Validators.required, Validators.minLength(6), Validators.maxLength(20)]],
      name: [null, [Validators.required, Validators.maxLength(30)]],
      mobileNumber: [null, [Validators.required, Validators.maxLength(20)]],
      identityCardNumber: [null, [Validators.maxLength(20)]],
      gender: [null, [Validators.maxLength(20)]],
      email: [null, [Validators.maxLength(50)]],
      enabled: [null, [Validators.required]],
      roles: [null]
    });
    const { account, password } = this._userForm.controls;

    // 生成用户表单补丁
    const { companyId, organizationId } = this._ngCortexService.data.passport!;
    const userFormPatch = {
      companyId: companyId,
      organizationId: organizationId,
      gender: '男',
      enabled: true,
      ...this.user
    };
    if (userFormPatch.organizationId) {
      userFormPatch.organizationId = userFormPatch.organizationId.toString();
    }

    // 定义用户表单规则
    if (userFormPatch.id) {
      account.disable();
      password.disable();
    }

    // 获取机构选项资源
    const organizationOptionUrl = 'source::@' + this._ngCortexService.coreService + '/api/organizations';
    this._organizationOptionResource = this._ngCortexService.getOptionResource(organizationOptionUrl, {
      map: this._generateOrganizationTreeNodeList,
      error: error => this._nzNotificationService.error('获取机构列表失败', getErrorMessage(error))
    });

    // 获取角色选项资源
    const roleOptionUrl = 'source::@' + this._ngCortexService.coreService + '/api/roles';
    this._roleOptionResource = this._ngCortexService.getOptionResource(roleOptionUrl, {
      map: (roleList: any[]) => roleList.sort((roleA, roleB) => roleA.sequence - roleB.sequence),
      error: error => this._nzNotificationService.error('获取角色列表失败', getErrorMessage(error))
    });

    // 生成原始选项
    if (userFormPatch.roles) {
      this._roleOptionResource.optionList = userFormPatch.roles;
    }

    // 更新用户表单值
    this._userForm.patchValue(userFormPatch);
  }

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

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

  /** 实体比较 */
  _entityCompareWith(): (entityA: any, entityB: any) => boolean {
    return (entityA: any, entityB: any) => (entityA && entityA.id) === (entityB && entityB.id);
  }

  /** 获取用户更新 */
  private _getUserUpdate(): any {
    const update = { ...this._userForm.getRawValue() };
    if (update.organizationId) {
      update.organizationId = Number(update.organizationId);
    }
    for (const key in update) {
      if (!isValid(update[key])) {
        update[key] = null;
      }
    }
    return getEntityUpdate(update, this.user) && { ...this.user, ...update };
  }

  /**
   * 提交用户
   * @param update 更新
   */
  private _submitUser(update: any): Observable<any> {
    const url = '@' + this._ngCortexService.coreService + '/api/users';
    const body = update;
    if (body.id) {
      return this._httpClient.put(url, body).pipe(tap(
        () => {
          this._nzNotificationService.success('更新用户成功', null!);
        },
        (httpErrorResponse: HttpErrorResponse) => {
          this._nzNotificationService.success('更新用户失败', getErrorMessage(httpErrorResponse));
        }));
    } else {
      return this._httpClient.post(url, body).pipe(tap(
        () => {
          this._nzNotificationService.success('新增用户成功', null!);
        },
        (httpErrorResponse: HttpErrorResponse) => {
          this._nzNotificationService.success('新增用户失败', getErrorMessage(httpErrorResponse));
        }));
    }
  }

  /**
   * 生成机构树形选项列表
   * @param organizationList 机构列表
   */
  private _generateOrganizationTreeNodeList(organizationList: any[]): NzTreeNodeOptions[] {
    const organizationTreeNodeOptionList: NzTreeNodeOptions[] = [];
    const organizationTreeNodeOptions: { [id: string]: NzTreeNodeOptions; } = {};
    organizationList
      .sort((organization1, organization2) => organization1.sequence - organization2.sequence)
      .forEach(organization => {
        const organizationTreeNodeOption = organizationTreeNodeOptions[organization.id] = organizationTreeNodeOptions[organization.id] || {};
        organizationTreeNodeOption.key = organization.id.toString();
        organizationTreeNodeOption.title = organization.name;
        if (!organizationTreeNodeOption.children) {
          organizationTreeNodeOption.isLeaf = true;
        }
        if (organization.parentId) {
          const parentOrganizationTreeNodeOption = organizationTreeNodeOptions[organization.parentId] = organizationTreeNodeOptions[organization.parentId] || {};
          if (!parentOrganizationTreeNodeOption.children) {
            delete parentOrganizationTreeNodeOption.isLeaf;
            parentOrganizationTreeNodeOption.children = [];
          }
          parentOrganizationTreeNodeOption.children.push(organizationTreeNodeOption);
        } else {
          organizationTreeNodeOptionList.push(organizationTreeNodeOption);
        }
      });
    return organizationTreeNodeOptionList;
  }

}
