import {
  ChangeDetectorRef,
  Component,
  EventEmitter,
  Input,
  OnChanges,
  OnDestroy,
  OnInit,
  Output,
  SimpleChanges,
  ViewEncapsulation
} from '@angular/core';
import { BixiI18nService } from '@bixi/core/i18n';
import { isNull } from '@bixi/core/utils';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { ColTemplateService } from './cols/col-template.service';
import { BixiPaginationListBase, IPagination, PAGE_SIZE_OPTIONS } from './pagination-list.base';
import { BixiTableUtilsService } from './table-utils.service';
import { BixiTableService } from './table.service';
import { EColType, ICheckboxCol, ICols, IFilterParams, IInternalCols, IRow, IRowKey, IRows, ISearchParams } from './table.type';

@Component({
  selector: 'bixi-table',
  exportAs: 'bixiTable',
  host: { '[class.bixi-table]': 'true' },
  templateUrl: 'table.component.html',
  encapsulation: ViewEncapsulation.None,
  preserveWhitespaces: true,
  providers: [ColTemplateService]
})
export class BixiTableComponent extends BixiPaginationListBase implements OnInit, OnChanges, OnDestroy {
  get showFooter(): boolean {
    if (this.frontPagination) {
      return this._rows.length > this.pagination.pageSize;
    } else {
      return this.pagination.total > this.pagination.pageSize;
    }
  }

  get total() {
    return {
      count: this.frontPagination ? this._rows.length : this.pagination.total
    };
  }

  get checkboxCol() {
    return this._cols.find(col => col.type === EColType.checkbox) as ICheckboxCol;
  }

  constructor(
    private tableService: BixiTableService,
    private tableUtilsService: BixiTableUtilsService,
    private i18n: BixiI18nService,
    private cdr: ChangeDetectorRef
  ) {
    super();
  }

  subscription = new Subscription();
  tableTotal: string;
  configVersion = 'randomString';
  _cols: IInternalCols = [];
  _rows: IRows = [];
  selectBoxWidth = '80px';
  isIndeterminate = false;
  isAllRowsChecked = false;

  private selectableRows: IRow[] = [];

  @Input() id: string;
  @Input() cols: ICols = [];
  @Input() rows: IRows = [];
  @Input() loading = false;
  @Input() frontPagination = false;
  @Input() scroll: { x?: string | null; y?: string | null } = { x: null, y: null };
  @Input() pageStartIndex = 1;
  @Input() pageSizeOptions = PAGE_SIZE_OPTIONS;
  @Input() pagination = { page: 1, pageSize: 10, total: 0 };
  @Input() filterParams: IFilterParams = {};
  @Input() selectedKeys: IRowKey[] = [];
  @Output() paginationChange = new EventEmitter<IPagination>();
  @Output() filterParamsChange = new EventEmitter<IFilterParams>();
  @Output() search = new EventEmitter<ISearchParams>();
  @Output() selectedKeysChange = new EventEmitter<IRowKey[]>();


  ngOnInit() {
    // 不使用 BixiPaginationListBase 中 ngOnInit()
    this.subscription.add(
      this.tableService.updateView$
        .pipe(
          filter(id => this.id === (id || this.id))
        )
        .subscribe(() => {
          this.forceUpdate();
        })
    );
    this.subscription.add(
      this.i18n.localeChange
        .subscribe(() => {
          this.tableTotal = this.i18n.getLocaleData('table.total', this.total);
          this.cdr.markForCheck();
        })
    );
  }

  ngOnChanges(changes: SimpleChanges) {
    const { cols, filterParams, rows, pagination, selectedKeys } = changes;
    if (cols || filterParams) {
      this._cols = this.tableUtilsService.genCols(
        cols?.currentValue || this.cols,
        filterParams?.currentValue || this.filterParams
      );
    }
    if (rows || selectedKeys) {
      this._rows = this.tableUtilsService.genRows(
        rows?.currentValue || this.rows,
        this._cols,
        selectedKeys?.currentValue || this.selectedKeys
      );
      this.checkSelectedStatus();
    }
    if (pagination) {
      this.pagination = pagination.currentValue;
      this.tableTotal = this.i18n.getLocaleData('table.total', this.total);
    }
  }

  onFilterChange(queryKey: string, val: NzSafeAny) {
    const filterParams = this.filterParams;
    if (isNull(val)) {
      delete filterParams[queryKey];
    } else {
      filterParams[queryKey] = !Array.isArray(val) ? [val] : val;
    }
    this.filterParamsChange.emit(filterParams);
    // sort 会影响数据的长度，这里应该重置到第一页
    // BAD SMELL!!
    this.onResetPagination();
    this.onSearch();
  }

  onSortChange(sortKey: string, val: NzSafeAny) {
    const filterParams = this.filterParams;
    if (isNull(val)) {
      delete filterParams[sortKey];
    } else {
      filterParams[sortKey] = val;
    }
    this.filterParamsChange.emit(filterParams);
    this.onSearch();
  }

  onSearch() {
    this.paginationChange.emit(this.pagination);
    this.search.emit({
      filterParams: this.filterParams,
      pagination: this.pagination
    });
  }

  onRowSelectedChange({ key, selected }: { key: IRowKey, selected: boolean }) {
    const selectedKeys = selected ? [...this.selectedKeys, key] : this.selectedKeys.filter(k => key !== k);
    this.selectedKeysChange.emit(selectedKeys);
  }

  onSelectAll(selected: boolean) {
    const changedKeys = this.selectableRows.map(row => row[this.checkboxCol.key]);
    const selectedKeys = this.tableUtilsService.updateSelectedKeys(selected, this.selectedKeys, changedKeys);
    this.selectedKeysChange.emit(selectedKeys);
  }

  checkSelectedStatus() {
    this.selectableRows = this._rows.filter(row => row._selectable);
    this.isIndeterminate = this.selectableRows.find(row => row._selected) && this.selectableRows.find(row => !row._selected);
    this.isAllRowsChecked = this.selectableRows.length > 0 && this.selectableRows.every(row => row._selected);
  }

  private forceUpdate() {
    this._cols = this.tableUtilsService.genCols(this.cols, this.filterParams);
    this.configVersion = new Date().getMilliseconds().toString();
    this.cdr.detectChanges();
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}
