import { Injectable } from '@angular/core';
import { get, has, isNullOrUndefined, isUndefined } from '@bixi/core/utils';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';

import {
  EColType,
  IAllowFnPure,
  ICheckboxCol,
  ICols,
  IFilterParams,
  IInternalCols,
  IRowKey,
  IRows
} from './table.type';
import { propGetter } from './utils';

@Injectable()
export class BixiTableUtilsService {

  genCols(cols: ICols = [], filterParams: IFilterParams = {}): IInternalCols {
    const _cols: IInternalCols = cols.map(e => {
      const filterKey = get(e, 'filter.key', e.key);
      const sortKey = get(e, 'sort.key', e.key);
      // 合并 filter
      if (e.filter && has(filterParams, filterKey)) {
        const selectList = filterParams[filterKey] ?? (e.filter.multiple ? [] : '');
        const options$ = e.filter.options instanceof Observable ? e.filter.options : of(e.filter.options || []);
        e.filter.options = [];
        options$
          .pipe(
            map(res => {
              res.forEach(o => {
                o.byDefault = selectList.includes(o.value);
              });
              return res;
            })
          )
          .subscribe(res => {
            e.filter = {
              ...e.filter,
              options: res
            };
          });
      }

      if (e.sort && has(filterParams, sortKey)) {
        e.sort.direction = filterParams[sortKey];
      }

      const visible = this.convertVisible(e.visible);

      if (e.type === EColType.checkbox) {
        e.width = e.width || '80px';
      }

      return {
        ...e,
        visible
      };
    });

    const visibleCols = _cols.filter(c => c.visible);
    visibleCols.forEach( (e, colIndex) => {
      if(e.fixed) {
        const colWidthList = visibleCols.map( c => Number((c.width || '50px').replace('px', '')));
        e._left = e.fixed === 'left' ? colWidthList.slice(0, colIndex).reduce((a, b) => a + b, 0) + 'px' : false;
        e._right = e.fixed === 'right' ? colWidthList.slice(colIndex + 1, colWidthList.length).reduce((a, b) => a + b, 0) + 'px' : false;
        e._leftLastFixed = e.fixed === 'left' && (colIndex !== visibleCols.length - 1 ) && visibleCols[colIndex + 1].fixed !== 'left';
        e._rightFirstFixed = e.fixed === 'right' && colIndex !== 0 && visibleCols[colIndex - 1].fixed !== 'right';
      }
    });

    return _cols;
  }

  genRows(rows: IRows = [], cols: ICols = [], selectedKeys: string[] = []) {
    const selectCol = cols.find(e => e.type === EColType.checkbox) as Required<ICheckboxCol>;
    if (selectCol) {
      const key = selectCol.key;
      rows.forEach((row, index) => {
        const selected = selectedKeys.includes(row[key]);
        const selectable = propGetter(row, selectCol, index, selectCol.selectable);
        row._selected = selected;
        row._selectable = isUndefined(selectable) ? true : selectable;
      });
    }
    return rows;
  }

  convertVisible(val: IAllowFnPure<boolean> | undefined): boolean {
    if (typeof val === 'function') {
      return val();
    } else if (isNullOrUndefined(val)) {
      return true;
    } else {
      return val as boolean;
    }
  }

  updateSelectedKeys(selected: boolean, origin: IRowKey[], inputs: IRowKey[]) {
    return selected ? this.mergeIntoSelectedKeys(origin, inputs) : this.removeFromSelectedKeys(origin, inputs);
  }

  mergeIntoSelectedKeys(origin: IRowKey[], inputs: IRowKey[]) {
    return [...new Set(origin.concat(inputs))];
  }

  removeFromSelectedKeys(origin: IRowKey[], inputs: IRowKey[]) {
    return origin.filter(e => !inputs.includes(e));
  }
}
