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 } 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 { NcApplicationEditorComponent } from '../nc-application-editor/nc-application-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; }>;
}

/** 应用管理页面 */
@Component({
  selector: 'nc-application',
  templateUrl: './nc-application.page.html',
  styleUrls: ['./nc-application.page.scss']
})
export class NcApplicationPage implements OnInit, AfterViewInit {

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

  /** 应用列表 */
  _applicationList: any[] = [];
  /** 应用额外属性 */
  _applicationExtras: any = {};
  /** 应用搜索表单 */
  _applicationSearchForm!: FormGroup;
  /** 应用加载状态 */
  _applicationLoading: boolean = false;
  /** 应用全部选中状态 */
  _applicationAllChecked: boolean = false;
  /** 应用模糊选中状态 */
  _applicationIndeterminate: boolean = false;
  /** 应用字段列表 */
  _applicationFieldList: Field[] = [
    { name: 'name', description: '应用名称', visible: true, width: '200px' },
    { name: 'code', description: '应用代码', visible: true, width: '200px' },
    { name: 'description', description: '描述', visible: true, width: '200px' },
    { name: 'icon', description: '图标', width: '200px' },
    { name: 'groupName', description: '分组名称', visible: true, width: '200px' },
    { name: 'common', description: '公共应用', visible: true, width: '200px' },
    { name: 'sequence', description: '顺序号', visible: true, align: 'right', width: '200px' },
    { name: 'createdBy', description: '创建人', width: '200px' },
    { name: 'createdTime', description: '创建时间', width: '200px' },
    { name: 'lastModifiedBy', description: '最后更新人', width: '200px' },
    { name: 'lastModifiedTime', description: '最后更新时间', width: '200px' }
  ];

  /** 取消获取应用列表 */
  private _cancelGetApplicationListFn = () => { };

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

  /** 初始化后 */
  ngOnInit(): void {
    this._applicationFieldList.forEach((field, index) => {
      field.defaultSequence = index + 1;
      field.defaultVisible = field.visible;
      field.defaultWidth = field.width;
    });
    this._applicationSearchForm = this._formBuilder.group({
      keywords: [null]
    });
    validateForm(this._applicationSearchForm, true).subscribe(() => this._getApplicationList().subscribe());
  }

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

  /** 查询应用列表 */
  _queryApplicationList(): void {
    validateForm(this._applicationSearchForm).subscribe(() => this._getApplicationList().subscribe());
  }

  /** 重新查询应用列表 */
  _requeryApplicationList(): void {
    this._queryApplicationList();
  }

  /** 重置应用搜索表单 */
  _resetApplicationSearchForm(): void {
    this._applicationSearchForm.reset();
    this._queryApplicationList();
  }

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

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

  /**
   * 选中所有应用列表
   * @param checked 选中状态
   */
  _checkAllApplicationList(checked: boolean): void {
    for (const id in this._applicationExtras) {
      this._applicationExtras[id].checked = checked;
    }
    this._applicationAllChecked = checked;
    this._applicationIndeterminate = false;
  }

  /** 更新应用选中状态 */
  _updateApplicationCheckStatus(): void {
    const checkedApplicationList = this._applicationList.filter(application => this._applicationExtras[application.id].checked);
    switch (checkedApplicationList.length) {
      case 0:
        this._applicationAllChecked = false;
        this._applicationIndeterminate = false;
        break;
      case this._applicationList.length:
        this._applicationAllChecked = true;
        this._applicationIndeterminate = false;
        break;
      default:
        this._applicationAllChecked = false;
        this._applicationIndeterminate = true;
    }
  }

  /** 新增应用 */
  _createApplication(): void {
    this._nzModalService.create({
      nzTitle: '新增应用',
      nzContent: NcApplicationEditorComponent,
      nzWidth: 520,
      nzOnOk: applicationEditor => {
        return applicationEditor.submit()
          .pipe(map(({ success, updated }) => {
            if (updated) {
              this._queryApplicationList();
            }
            return success;
          }))
          .toPromise();
      }
    });
  }

  /**
   * 编辑应用
   * @param application 应用
   */
  _updateApplication(application: any): void {
    this._nzModalService.create({
      nzTitle: '编辑应用',
      nzContent: NcApplicationEditorComponent,
      nzComponentParams: {
        application: application
      },
      nzWidth: 520,
      nzOnOk: applicationEditor => {
        return applicationEditor.submit()
          .pipe(map(({ success, updated }) => {
            if (updated) {
              this._queryApplicationList();
            }
            return success;
          }))
          .toPromise();
      }
    });
  }

  /**
   * 删除应用
   * @param application 应用
   */
  _deleteApplication(application: any): void {
    let cancelDeleteApplicationFn = () => { };
    this._nzModalService.confirm({
      nzTitle: '删除确认',
      nzContent: '是否确认删除应用 “' + application.name + '”',
      nzOnOk: () => {
        return new Observable(subscriber => {
          const url = '@<%= getSeparatorCase(serviceName) %>/api/applications/' + application.id;
          const deleteApplicationSubscription = this._httpClient.delete(url, { responseType: 'text' })
            .pipe(map(() => {
              this._queryApplicationList();
              return true;
            }))
            .pipe(catchError((httpErrorResponse: HttpErrorResponse) => {
              this._nzNotificationService.error('删除应用失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
              return of(false);
            }))
            .subscribe(subscriber);
          cancelDeleteApplicationFn = () => {
            deleteApplicationSubscription.unsubscribe();
          };
        })
          .toPromise();
      },
      nzOnCancel: () => {
        cancelDeleteApplicationFn();
      }
    });
  }

  /** 获取应用列表 */
  private _getApplicationList(): Observable<HttpErrorResponse | Error | void> {
    this._cancelGetApplicationListFn();
    return new Observable(subscriber => {
      if (this._applicationSearchForm.valid) {
        this._applicationLoading = true;
        const completeGetApplicationListFn = () => {
          this._cancelGetApplicationListFn = () => { };
          this._applicationLoading = false;
        };
        const url = '@<%= getSeparatorCase(serviceName) %>/api/applications';
        const params: any = { ...this._applicationSearchForm.getRawValue() };
        for (const key in params) {
          if (!isValid(params[key])) {
            delete params[key];
          }
        }
        const getApplicationListSubscription = this._httpClient.get<any[]>(url, { params })
          .pipe(tap(() => subscriber.next(undefined)))
          .pipe(catchError((httpErrorResponse: HttpErrorResponse) => {
            this._nzNotificationService.error('获取应用列表失败', httpErrorResponse.error && httpErrorResponse.error.message || httpErrorResponse.statusText);
            subscriber.next(httpErrorResponse);
            return of([]);
          }))
          .subscribe(applicationList => {
            completeGetApplicationListFn();
            const applicationExtras: any = {};
            applicationList.forEach(application => {
              applicationExtras[application.id] = { checked: false };
            });
            this._applicationList = applicationList;
            this._applicationExtras = applicationExtras;
            this._applicationAllChecked = false;
            this._applicationIndeterminate = false;
            subscriber.complete();
          });
        this._cancelGetApplicationListFn = () => {
          getApplicationListSubscription.unsubscribe();
          completeGetApplicationListFn();
          subscriber.next(new Error('取消获取应用列表'));
          subscriber.complete();
        };
      } else {
        subscriber.next(new Error('筛选应用不合法'));
        subscriber.complete();
      }
    });
  }

}
