import { FiltersMap, ReportBI, TaskState } from '@wix/bex-core';

import { NestedCollectionState } from './NestedCollectionState';
import { TableVirtualState } from '../TableVirtualState';
import { NestedTableNodeState } from './NestedTableNodeState';
import { action, computed, makeObservable } from 'mobx';
import { NestedTableState } from './NestedTableState';
import { NestedTableDragAndDropState } from '../../components/NestedTableDragAndDrop/NestedTableDragAndDropState';
import { getNodeOrLeaf } from './getNodeOrLeaf';
import { NestedTableNodeStatePublicAPI } from './NestedTableNodeStatePublicAPI';
import { NestedTableNestedModeStateBIReporter } from './NestedTableNestedModeStateBIReporter';
import { addEventListener } from '@wix/bex-core/util';
import { createNestedOptimisticActions } from './createNestedOptimisticActions';

export interface NestedTableNestedModeStateBaseParams {}

export interface NestedTableNestedModeStateParams<C extends string>
  extends NestedTableNestedModeStateBaseParams {
  wrapper: NestedTableState<C>;
  reportBi: ReportBI;
}

export class NestedTableNestedModeState<C extends string = string> {
  readonly wrapper;
  readonly container;
  readonly bi: NestedTableNestedModeStateBIReporter<C>;
  readonly nestedCollection: NestedCollectionState;
  readonly virtual;

  readonly initTask = new TaskState();

  readonly root: NestedTableNodeState<C, any, any>;

  nestedTableDragAndDrop: NestedTableDragAndDropState<C> | null = null;

  constructor(params: NestedTableNestedModeStateParams<C>) {
    const { wrapper } = params;
    this.wrapper = wrapper;
    this.container = wrapper.container;

    this.bi = new NestedTableNestedModeStateBIReporter(this);

    const rootLevelDescriptor = this.wrapper.levels[0];

    if (rootLevelDescriptor == null) {
      throw new Error('NestedTableState must have at least one level');
    }

    const nestedOptimisticActionsState = {
      parentId: 'null',
      sequences: wrapper.sequences,
      query: wrapper.query,
      levelDescriptor: rootLevelDescriptor,
    };

    const rootNodeCollection = rootLevelDescriptor.createCollection(
      nestedOptimisticActionsState,
    );

    createNestedOptimisticActions(
      this.container,
      rootNodeCollection.collection,
      {
        ...nestedOptimisticActionsState,
        parentFilter: rootNodeCollection.parentFilter,
      },
    );

    this.nestedCollection = new NestedCollectionState({
      container: this.container,
      levels: wrapper.levels,
      query: wrapper.query,
      nodeCollection: rootNodeCollection,
      sequences: wrapper.sequences,
    });

    this.root = new NestedTableNodeState({
      nestedTable: this,
      node: this.nestedCollection.root,
      levelDescriptor: rootLevelDescriptor,
      parentNode: null,
      parent: null,
      preRegister: true,
    });

    this.virtual = new TableVirtualState({
      table: this,
      getVirtualItemKey: (item) =>
        [item.id, item.item.state.parent?.id].filter(Boolean).join('_'),
    });

    makeObservable(this, {
      hasExpandableItems: computed,
    });
  }

  getCollectionSnapshot() {
    const { toolbar } = this;

    const {
      collection: {
        result: { originQuery, total },
      },
    } = toolbar;

    return {
      viewShown: toolbar.viewsState?.views.currentView?.id,
      tabShown: toolbar.getCurrentTabName(),
      sortOrderApplied: originQuery.sort?.length
        ? JSON.stringify(originQuery.sort)
        : undefined,
      filtersApplied: JSON.stringify(originQuery.filtersKey),
      filteredListSizeShown: total,
    };
  }

  get multi() {
    return this.toolbar.multi;
  }

  get tableState() {
    return this.wrapper.tableState;
  }

  get toolbar() {
    return this.wrapper.toolbar;
  }

  get total() {
    return this.nestedCollection.total;
  }

  get originQuery() {
    return this.toolbar.collection.originQuery;
  }

  get keyedItems() {
    return this.root.keyedItems;
  }

  get _keyedItemsMap() {
    return this.root._keyedItemsMap;
  }

  _indentationWidth = 28;
  _parentCellPaddingStart = 24;

  getKeyedItem(key: string) {
    return this.root.getKeyedItem(key);
  }

  getKeyedItemByIndex(ind: number) {
    return this.root.getKeyedItemByIndex(ind);
  }

  getNode<TD, FD extends FiltersMap>(
    key: string,
  ): NestedTableNodeStatePublicAPI<C, TD, FD> | undefined {
    const keyedItem = this.root._allKeyedItemsMap.get(key);
    if (keyedItem == null || keyedItem.item.originalKeyedItem == null) {
      return undefined;
    }
    const { item } = keyedItem;
    return getNodeOrLeaf(item.state, item.originalKeyedItem) as
      | NestedTableNodeState<C, TD, FD>
      | undefined;
  }

  getAllNodes<TD, FD extends FiltersMap>(): NestedTableNodeStatePublicAPI<
    C,
    TD,
    FD
  >[] {
    return this.root.getDescendants();
  }

  get expandAll() {
    return this.nestedCollection.expandAll;
  }

  get isFetchAllAborted() {
    return this.nestedCollection.isFetchAllAborted;
  }

  get isAllCollapsed() {
    return this.root.getDescendants().every((node) => !node.expanded);
  }

  get hasExpandableItems() {
    return this.root.hasExpandableItems;
  }

  get showEmptyState() {
    return this.root.showEmptyState;
  }

  get hasAvailableItems() {
    return this.toolbar.multi.hasAvailableItems;
  }

  get resultOriginQuerySearch() {
    return this.toolbar.multi.resultOriginQuerySearch;
  }

  get hasNonPersistentActiveFilters() {
    return this.toolbar.multi.hasNonPersistentActiveFilters;
  }

  get showErrorState() {
    const { initTask } = this;
    return initTask.status.isError;
  }

  get showLoadingState() {
    return this.initTask.status.isLoading;
  }

  retryErrorState = action(() => {
    const { initTask } = this;

    if (initTask.status.isError) {
      initTask.runOnce();
    }
  });

  expandCollapseAll = action(() => {
    this.bi.reportExpandCollapseAll();
    const isAllCollapsed = this.isAllCollapsed;

    const nodes = this.root.getDescendants();

    for (const node of nodes) {
      if (node.expanded !== isAllCollapsed) {
        const nodeState = (node as NestedTableNodeState<C, any, any>).node;
        if (!nodeState) {
          continue;
        }
        nodeState._expanded = !nodeState._expanded;
      }
    }
  });

  get errorStatus() {
    const { initTask } = this;
    return initTask.errorStatus;
  }

  get query() {
    return this.wrapper.query;
  }

  get levels() {
    return this.wrapper.levels;
  }

  init() {
    const { initTask, wrapper, root, nestedCollection } = this;

    const attemptFetchAllState = wrapper._attemptFetchAllAndPopulateCache();

    const disposers = [
      addEventListener(
        root.collection.hooks,
        'beforeInitialFetch',
        () => attemptFetchAllState.initTask.status.promise,
      ),
      addEventListener(
        wrapper.rootNodeCollection.collection.hooks,
        'beforeInitialFetch',
        () => attemptFetchAllState.initTask.status.promise,
      ),
      attemptFetchAllState.init(),
      wrapper.init(),
      root.init(),
      nestedCollection.init(),
    ];

    initTask.runOnce(async () => {
      wrapper.initTask.runOnce();
      attemptFetchAllState.initTask.runOnce();
      root.initTask.runOnce();

      await attemptFetchAllState.initTask.status.promise;
      await root.initTask.status.promise;
      await wrapper.initTask.status.promise;
    });

    return () => {
      disposers.forEach((d) => d());
    };
  }
}
