import {
  WixPatternsContainer,
  CollectionState,
  FiltersMap,
  KeyedItem,
} from '@wix/bex-core';
import { computed, makeObservable } from 'mobx';
import { ToolbarCollectionState } from './ToolbarCollectionState';
import {
  FoldersAndItemsCollectionsState,
  FoldersAndItemsCollectionsStateInitParams,
  PartialAllFilters,
} from './FoldersAndItemsCollectionsState';
import { TableState } from './TableState';
import { TableVirtualState } from './TableVirtualState';
import { ICollectionComponentState } from './ICollectionComponentState';

export interface TableFoldersStateParams<
  T1,
  F1 extends FiltersMap,
  T2,
  F2 extends FiltersMap,
> {
  readonly items: CollectionState<T1, F1>;
  readonly folders: CollectionState<T2, F2>;
  readonly container: WixPatternsContainer;
  readonly toolbar?: ToolbarCollectionState<T1, PartialAllFilters<F1, F2>>;
}

interface Item<T1> {
  _tag: 'item';
  originalKeyedItem: KeyedItem<T1>;
}

interface Folder<T2> {
  _tag: 'folder';
  originalKeyedItem: KeyedItem<T2>;
}

export type RowItem<T1, T2> = Item<T1> | Folder<T2>;
export type RowKeyedItem<T1, T2> = KeyedItem<RowItem<T1, T2>>;

export function isItemKeyedItem<T1, T2>(
  keyedItem: RowKeyedItem<T1, T2>,
): keyedItem is KeyedItem<Item<T1>> {
  return keyedItem.item._tag === 'item';
}

export function isFolderKeyedItem<T1, T2>(
  keyedItem: RowKeyedItem<T1, T2>,
): keyedItem is KeyedItem<Folder<T2>> {
  return keyedItem.item._tag === 'folder';
}

interface TableFoldersStateInitParams
  extends FoldersAndItemsCollectionsStateInitParams {}

export interface TableFoldersStatePublicAPI<
  T1,
  F1 extends FiltersMap,
  T2,
  F2 extends FiltersMap,
> {
  /**
   * Underlying [ToolbarCollectionState](./?path=/story/common-state--toolbarcollectionstate) instance
   */
  readonly toolbar: ToolbarCollectionState<T1, {}>;
  /**
   * Underlying [TableState](./?path=/story/base-components-collections-table-tablestate--tablestate) instance
   */
  readonly tableState: TableState<T1, {}>;

  /**
   * Underlying [CollectionState](./?path=/story/common-state--collectionstate) instances for folders and items
   */
  readonly collections: {
    readonly folders: CollectionState<T2, F2>;
    readonly items: CollectionState<T1, F1>;
  };
}

export class TableFoldersState<
  T1,
  F1 extends FiltersMap,
  T2,
  F2 extends FiltersMap,
> implements
    ICollectionComponentState,
    TableFoldersStatePublicAPI<T1, F1, T2, F2>
{
  readonly container;
  readonly table;
  readonly toolbar;
  readonly virtual;
  readonly collections;

  constructor(params: TableFoldersStateParams<T1, F1, T2, F2>) {
    this.container = params.container;

    this.virtual = new TableVirtualState({
      table: this,
    });

    this.toolbar =
      params.toolbar ??
      new ToolbarCollectionState({
        collection: params.items as CollectionState<T1, {}> as CollectionState<
          T1,
          PartialAllFilters<F1, F2>
        >,
        container: params.container,
        componentType: 'TableFolders',
      });

    this.collections = new FoldersAndItemsCollectionsState({
      ...params,
      toolbar: this.toolbar,
    });

    this.table = new TableState({
      container: this.container,
      collection: params.items,
      toolbar: this.toolbar as ToolbarCollectionState<T1, {}>,
    });

    makeObservable(this, {
      showItemsSection: computed,
      showFoldersSection: computed,
      keyedItems: computed,
    });

    this.toolbar.bulk.shouldIncludeCollection = (collection) => {
      if (collection === this.collections.folders) {
        // Only exclude folders if they're explicitly hidden due to filters
        return !this.collections.hasItemsOnlyActiveFilters;
      }
      if (collection === this.collections.items) {
        // Only exclude items if they're explicitly hidden due to filters
        return !this.collections.hasFoldersOnlyActiveFilters;
      }
      return true;
    };
  }

  get showItemsSection() {
    return (
      !this.collections.items.result.isEmptyAndNotFetching &&
      !this.collections.hasFoldersOnlyActiveFilters &&
      (!this.showFoldersSection || this.collections.folders.fetchedLastPage)
    );
  }

  get showFoldersSection() {
    return (
      !this.collections.folders.result.isEmptyAndNotFetching &&
      !this.collections.hasItemsOnlyActiveFilters
    );
  }

  get tableState() {
    return this.table;
  }

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

  get hasAvailableItems() {
    return this.collections.hasAvailableItems;
  }

  get resultOriginQuerySearch() {
    return this.collections.resultOriginQuerySearch;
  }

  get hasNonPersistentActiveFilters() {
    return this.collections.hasNonPersistentActiveFilters;
  }

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

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

  get showLoadingState() {
    const { initTask, toolbar } = this;
    return (
      initTask.status.isIdle ||
      initTask.status.isLoading ||
      toolbar.multi.showLoadingState
    );
  }

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

  retryErrorState = () => this.toolbar.retryErrorState();

  get keyedItems(): RowKeyedItem<T1, T2>[] {
    const {
      showFoldersSection,
      showItemsSection,
      collections: { folders, items },
    } = this;
    return [
      ...(showFoldersSection
        ? folders.result.keyedItems.map((keyedItem) => ({
            ...keyedItem,
            item: {
              _tag: 'folder' as const,
              originalKeyedItem: keyedItem,
            },
          }))
        : []),
      ...(showItemsSection
        ? items.result.keyedItems.map((keyedItem) => ({
            ...keyedItem,
            item: {
              _tag: 'item' as const,
              originalKeyedItem: keyedItem,
            },
            originalKeyedItem: keyedItem,
          }))
        : []),
    ];
  }

  init(params: TableFoldersStateInitParams) {
    const disposers = [this.collections.init(params)];

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