import { BaseEntity, ErrorCallBack } from '../generics/baseentity';
import { BaseEntityListWrapper } from './baseentitylist.wrapper';
import { BaseEntityWrapper } from './baseentity.wrapper';
import { forkJoin, Subject } from 'rxjs';
import { EN_EntityEditingStates } from '../../consts';

export const mdLOAD: number = 1;
export const mdSAVE: number = 2;

export interface BaseEntityDetail {
  baseEntityList?: BaseEntityListWrapper<BaseEntity>;
  baseEntityWrapper?: BaseEntityWrapper<BaseEntity>;
  callBack?: (entity: BaseEntity[] | BaseEntity) => void;
  errorCallBack?: ErrorCallBack;
  completeCallBack?: () => void;
  filterKeyField: string;
  masterField4Filter: string;
  mode: number;
}


export class BaseEntityDetailList {
  public items: BaseEntityDetail[] = [];

  public observList$: Subject<void>[] = [];

  public get isEmpty(): boolean {
    return this.items.length === 0;
  }


  public get isEmpty4Save(): boolean {
    return this.observList$.length === 0;

  }


  private _register(_mode: number, _baseEntityList: BaseEntityListWrapper<BaseEntity>, _baseEntityWrapper: BaseEntityWrapper<BaseEntity>, _filterKeyField: string | string[],
    _callBack?: (entity: BaseEntity[] | BaseEntity) => void, _errorCallBack?: ErrorCallBack, _completeCallBack?: () => void): BaseEntityDetail {

    let filterKeyField: string = null;
    let masterField4Filter: string = null;
    if (Array.isArray(_filterKeyField)) {
      filterKeyField = _filterKeyField[0];
      if (_filterKeyField.length > 1) masterField4Filter = _filterKeyField[1];
    } else filterKeyField = _filterKeyField;
    const item: BaseEntityDetail = {
      baseEntityList: _baseEntityList,
      baseEntityWrapper: _baseEntityWrapper,
      filterKeyField: filterKeyField,
      masterField4Filter: masterField4Filter,
      callBack: _callBack,
      errorCallBack: _errorCallBack,
      completeCallBack: _completeCallBack,
      mode: _mode
    };
    this.items.push(item);
    if (_baseEntityWrapper) {
      _baseEntityWrapper.hierarchyLevel++;
      _baseEntityWrapper.editModeDefault = EN_EntityEditingStates.Edit; //to saving with main automatically
    }
    if (_baseEntityList) _baseEntityList.hierarchyLevel++;
    return item;
  }


  public register(_baseEntityList: BaseEntityListWrapper<BaseEntity>, _baseEntityWrapper: BaseEntityWrapper<BaseEntity>, _filterKeyField: string | string[],
    _callBack?: (entity: BaseEntity[] | BaseEntity) => void, _errorCallBack?: ErrorCallBack, _completeCallBack?: () => void): BaseEntityDetail {
    return this._register(mdLOAD + mdSAVE, _baseEntityList, _baseEntityWrapper, _filterKeyField, _callBack, _errorCallBack, _completeCallBack);
  }


  public register4Load(_baseEntityList: BaseEntityListWrapper<BaseEntity>, _baseEntityWrapper: BaseEntityWrapper<BaseEntity>, _filterKeyField: string | string[],
    _callBack?: (entity: BaseEntity[] | BaseEntity) => void, _errorCallBack?: ErrorCallBack, _completeCallBack?: () => void): BaseEntityDetail {
    return this._register(mdLOAD, _baseEntityList, _baseEntityWrapper, _filterKeyField, _callBack, _errorCallBack, _completeCallBack);
  }


  public register4Save(_baseEntityList: BaseEntityListWrapper<BaseEntity>, _baseEntityWrapper: BaseEntityWrapper<BaseEntity>, _filterKeyField: string | string[],
    _callBack?: (entity: BaseEntity[] | BaseEntity) => void, _errorCallBack?: ErrorCallBack, _completeCallBack?: () => void): BaseEntityDetail {
    return this._register(mdSAVE, _baseEntityList, _baseEntityWrapper, _filterKeyField, _callBack, _errorCallBack, _completeCallBack);
  }


  public prepareObservables(observList$: Subject<void>[], mode: number) {
    this.items.forEach((item: BaseEntityDetail) => {
      // tslint:disable-next-line: no-bitwise
      if (item.mode & mode) observList$.push(new Subject());
    });
  }


  public prepareObservables4SaveAutonome(_observList$: Subject<void>[], mode: number, callBack: () => void) {
    this.observList$ = [];
    this.prepareObservables(this.observList$, mode);
    if (this.observList$.length === 0) return;
    const observListAutonome$: Subject<void> = new Subject();
    _observList$.push(observListAutonome$);
    forkJoin(this.observList$).subscribe(
      () => {
        // console.log('******Details next');
        if (callBack) callBack();
        observListAutonome$.next();
      }, (error: any) => observListAutonome$.error(error)
      , () => {
        // console.log('******Details complete');
        observListAutonome$.complete();
      }
    );
  }


  public new() {
    this.items.forEach((list: BaseEntityDetail) => list.baseEntityList ? list.baseEntityList.clear() : list.baseEntityWrapper.new());
  }


  public clear(useNull: boolean = false) {
    this.items.forEach((list: BaseEntityDetail) => {
      if (list.baseEntityList) list.baseEntityList.clear();
      else if (list.baseEntityWrapper.entity) list.baseEntityWrapper.entity.clear(useNull);
    });
  }


  public cancel() {
    this.items.forEach((list: BaseEntityDetail) => {
      if (list.baseEntityList) list.baseEntityList.clear();
      else {
        list.baseEntityWrapper.resetEditMode();
        if (list.baseEntityWrapper.entity) list.baseEntityWrapper.entity.clear();
        // list.baseEntityWrapper.cancel();
      }
    });
  }


  public changed(): boolean {
    return this.items.some((list: BaseEntityDetail) => list.baseEntityList ? list.baseEntityList.changed : list.baseEntityWrapper.entityWasChanged);
  }


  public isValidInput(): boolean {
    return this.items.every((list: BaseEntityDetail) => list.baseEntityList ? list.baseEntityList.isValidInput : list.baseEntityWrapper.isValidInput);
  }


  public load(masterEntityWrapper: BaseEntityWrapper<BaseEntity>, observList$?: Subject<void>[], baseIndex: number = 0, onlyDetailItem: BaseEntityListWrapper<BaseEntity> | BaseEntityWrapper<BaseEntity> = null) {
    // tslint:disable-next-line: no-bitwise
    this.items.filter(x => (x.mode & mdLOAD)).forEach((item: BaseEntityDetail, index: number) => {
      const indexObservable = index + baseIndex;
      // observList$.push(new Subject());
      if (masterEntityWrapper.entityId) {
        if (item.baseEntityList && (!onlyDetailItem || onlyDetailItem === item.baseEntityList)) {
          if (item.filterKeyField && item.baseEntityList.filter) item.baseEntityList.filter[item.filterKeyField] = masterEntityWrapper.entity[item.masterField4Filter ?? 'entityId'];
          item.baseEntityList.load(
            (entityList: BaseEntity[]) => {
              console.log('!! next load detail No.' + indexObservable + ': ' + (item.baseEntityList ? item.baseEntityList.translate.instant(item.baseEntityList._listName) : item.baseEntityWrapper.entity.typeName));
              if (item.callBack) item.callBack(entityList);
              if (observList$) observList$[indexObservable].next();
            }, (error: any) => {
              if (item.errorCallBack) item.errorCallBack(error);
              if (observList$) observList$[indexObservable].error(error);
            }, () => {
              console.log('!! complete load detail No.' + indexObservable + ': ' + (item.baseEntityList ? item.baseEntityList.translate.instant(item.baseEntityList._listName) : item.baseEntityWrapper.entity.typeName));
              if (item.completeCallBack) item.completeCallBack();
              if (observList$) observList$[indexObservable].complete();
            }
          );
        } else if (item.baseEntityWrapper && (!onlyDetailItem || onlyDetailItem === item.baseEntityWrapper)) {
          item.baseEntityWrapper.load(masterEntityWrapper.entity[item.filterKeyField ?? 'entityId'],
            (entity: BaseEntity) => {
              console.log('!! next load detail No.' + indexObservable + ': ' + (item.baseEntityList ? item.baseEntityList.translate.instant(item.baseEntityList._listName) : item.baseEntityWrapper.entity.typeName));
              if (item.callBack) item.callBack(entity);
              if (observList$) observList$[indexObservable].next();
            }, (error: any) => {
              if (item.errorCallBack) item.errorCallBack(error);
              if (observList$) observList$[indexObservable].error(error);
            }, () => {
              console.log('!! complete load detail No.' + indexObservable + ': ' + (item.baseEntityList ? item.baseEntityList.translate.instant(item.baseEntityList._listName) : item.baseEntityWrapper.entity.typeName));
              if (item.completeCallBack) item.completeCallBack();
              if (observList$) observList$[indexObservable].complete();
            }
          );
        }
      } else {
        if (item.baseEntityList) item.baseEntityList.clear();
        else item.baseEntityWrapper.new();
        if (observList$) observList$[indexObservable].next();
        if (observList$) observList$[indexObservable].complete();
      }
    });
  }


  private itemSave(masterEntityWrapper: BaseEntityWrapper<BaseEntity>, item: BaseEntityDetail, callback: () => void, errorcallback: (error: any) => void, completecallback: () => void) {
    if (item.baseEntityList) {
      item.baseEntityList.items.forEach(it => {
        if (it.isNew()) it[item.filterKeyField] = masterEntityWrapper.entity[item.masterField4Filter ?? 'entityId'];
      });
      item.baseEntityList.save(
        () => {
          if (item.callBack) item.callBack(item.baseEntityList.items);
          if (callback) callback();
        },
        (error: any) => {
          if (item.errorCallBack) item.errorCallBack(item.baseEntityList.items);
          if (errorcallback) errorcallback(error);
        },
        () => {
          if (item.completeCallBack) item.completeCallBack();
          if (completecallback) completecallback();
        }
      );
    } else {
      if (item.baseEntityWrapper.isNew) item.baseEntityWrapper.entity[item.filterKeyField] = masterEntityWrapper.entity[item.masterField4Filter ?? 'entityId'];
      item.baseEntityWrapper.save(
        () => {
          if (item.callBack) item.callBack(item.baseEntityWrapper.entity);
          if (callback) callback();
        },
        (error: any) => {
          if (item.errorCallBack) item.errorCallBack(item.baseEntityWrapper.entity);
          if (errorcallback) errorcallback(error);
        },
        () => {
          if (item.completeCallBack) item.completeCallBack();
          if (completecallback) completecallback();
        }
      );
    }
  }


  public batchSave(masterEntityWrapper: BaseEntityWrapper<BaseEntity>) {
    this.save(masterEntityWrapper, this.observList$, 0);
  }


  public save(masterEntityWrapper: BaseEntityWrapper<BaseEntity>, observList$: Subject<void>[], baseIndex: number) {
    // tslint:disable-next-line: no-bitwise
    this.items.filter(x => x.mode & mdSAVE).forEach((item: BaseEntityDetail, index: number) => {
      const indexObservable = index + baseIndex;
      if (masterEntityWrapper.entityId) { //details list can be saved only if id>0
        this.itemSave(masterEntityWrapper, item,
          () => {
            console.log('!! next save detail No.' + indexObservable + ': ' + (item.baseEntityList ? item.baseEntityList.translate.instant(item.baseEntityList._listName) : item.baseEntityWrapper.entity.typeName));
            observList$[indexObservable].next();
          },
          (error: any) => observList$[indexObservable].error(error),
          () => {
            console.log('!! complete save detail No.' + indexObservable + ': ' + (item.baseEntityList ? item.baseEntityList.translate.instant(item.baseEntityList._listName) : item.baseEntityWrapper.entity.typeName));
            observList$[indexObservable].complete();
          }
        );
      } else {
        if (item.baseEntityList) item.baseEntityList.clear();
        else item.baseEntityWrapper.new();
        observList$[indexObservable].next();
        observList$[indexObservable].complete();
      }
    });
  }

}
