import {
  WixPatternsContainer,
  QueryState,
  CollectionOptimisticSequenceExecutor,
} from '@wix/bex-core';
import { makeObservable, observable } from 'mobx';
import { NestedCollectionNodeState } from './NestedCollectionNodeState';
import { NestedCollectionLevelDescriptor } from './NestedCollectionLevelDescriptor';
import { NodeCollection } from './types';
import { EventEmitter } from 'events';
import { TypedEmitter } from '@wix/bex-core/events';

export interface NestedCollectionStateBaseParams {
  levels: NestedCollectionLevelDescriptor<any, any>[];
  query: QueryState<{}>;
  nodeCollection?: NodeCollection<any, any>;
}

export interface NestedCollectionStateParams
  extends NestedCollectionStateBaseParams {
  readonly container: WixPatternsContainer;
  readonly sequences: Map<string, CollectionOptimisticSequenceExecutor>;
}

export class NestedCollectionState {
  readonly container;

  readonly levels;

  isFetchAllAborted = null as null | boolean;

  _fetchAllTotal = undefined as undefined | number;

  expandAll = true;
  total = 0;

  readonly root: NestedCollectionNodeState<any, any>;
  readonly query;
  readonly sequences;

  readonly events = new EventEmitter() as TypedEmitter<{
    dispose: (() => void) | undefined;
  }>;

  constructor(params: NestedCollectionStateParams) {
    const { container } = params;
    this.container = container;
    this.sequences = params.sequences;

    const rootLevelDescriptor = params.levels[0];

    this.levels = params.levels;
    this.query = params.query;

    this.root = new NestedCollectionNodeState({
      nestedCollection: this,
      parentNode: null,
      parent: null,
      levelDescriptor: rootLevelDescriptor,
      nodeCollection: params.nodeCollection,
    });

    const parentFilter = this.root.parentFilter;
    if (parentFilter != null) {
      parentFilter.setValue(['null']);
      parentFilter.persistent = true;
    }

    makeObservable(this, {
      total: observable.ref,
    });
  }

  init() {
    return () => {
      const disposeListeners = this.events.listeners('dispose');
      this.events.removeAllListeners('dispose');
      return disposeListeners.forEach((d) => d?.());
    };
  }
}
