import {
  CollectionState,
  Filter,
  FiltersMap,
  syncWithSharedQuery,
} from '@wix/bex-core';
import {
  ToolbarCollectionState,
  ToolbarCollectionStateParamsInitParams,
} from './ToolbarCollectionState';
import { addEventListener } from '@wix/bex-core/util';
import { computed, makeObservable } from 'mobx';

export type PartialAllFilters<
  F1 extends FiltersMap,
  F2 extends FiltersMap,
> = Partial<F1 & F2>;

const isActiveFilterAndNotInCollection =
  <T>({ query: { filtersArray } }: CollectionState<T, {}>) =>
  (f1: Filter<unknown>) =>
    !f1.isEmpty && !filtersArray.some((f2) => f2 === f1);

export interface FoldersAndItemsCollectionsStateInitParams
  extends ToolbarCollectionStateParamsInitParams {}

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

export class FoldersAndItemsCollectionsState<
  T1,
  F1 extends FiltersMap,
  T2,
  F2 extends FiltersMap,
> {
  readonly toolbar;
  readonly items;
  readonly folders;

  constructor(params: FoldersAndItemsCollectionsStateParams<T1, F1, T2, F2>) {
    this.toolbar = params.toolbar;
    this.items = params.items;
    this.folders = params.folders;

    this.toolbar.multi.collections.push(
      this.folders as CollectionState<unknown, {}>,
    );

    syncWithSharedQuery(this.folders.query, this.items.query);
    this.items.bulkSelect.useNewSelectAllLogic = true;
    this.folders.bulkSelect.useNewSelectAllLogic = true;

    makeObservable(this, {
      hasItemsOnlyActiveFilters: computed,
      hasFoldersOnlyActiveFilters: computed,
      showEmptyState: computed,
      hasSelectedFolders: computed,
    });
  }

  get hasFoldersOnlyActiveFilters() {
    const { folders, items } = this;
    return folders.query.filtersArray.some(
      isActiveFilterAndNotInCollection(items),
    );
  }

  get hasItemsOnlyActiveFilters() {
    const { folders, items } = this;
    return items.query.filtersArray.some(
      isActiveFilterAndNotInCollection(folders),
    );
  }

  get showEmptyState() {
    const {
      toolbar: {
        multi: { showEmptyState },
      },
      items,
      folders,
      hasFoldersOnlyActiveFilters,
      hasItemsOnlyActiveFilters,
    } = this;
    return (
      showEmptyState ||
      (folders.showEmptyState && hasFoldersOnlyActiveFilters) ||
      (items.showEmptyState && hasItemsOnlyActiveFilters)
    );
  }

  get hasAvailableItems() {
    const {
      toolbar: {
        multi: { hasAvailableItems },
      },
      items,
      folders,
    } = this;
    return (
      hasAvailableItems || folders.hasAvailableItems || items.hasAvailableItems
    );
  }

  get resultOriginQuerySearch() {
    const {
      toolbar: {
        multi: { resultOriginQuerySearch },
      },
    } = this;
    return resultOriginQuerySearch;
  }

  get hasNonPersistentActiveFilters() {
    const {
      toolbar: {
        multi: { hasNonPersistentActiveFilters },
      },
    } = this;
    return hasNonPersistentActiveFilters;
  }

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

  get hasSelectedFolders() {
    return !!this.folders.bulkSelect.selectedIds.length;
  }

  init(initParams: FoldersAndItemsCollectionsStateInitParams) {
    const { items, folders, toolbar } = this;
    const disposers = [
      toolbar.init(initParams),
      addEventListener(items.emitter, 'search', ({ clearResult }) => {
        folders._runSearch(clearResult);
      }),
      addEventListener(items.emitter, 'clearSearch', () => {
        folders.clearResultAndMoveToStart();
      }),
      addEventListener(items.emitter, 'sort', (params) => {
        folders._runSort(params);
      }),
      addEventListener(items.emitter, 'beforeSortStart', (params) => {
        folders.emitter.emit('beforeSortStart', params);
      }),
      addEventListener(items.emitter, 'sortStart', (params) => {
        folders.emitter.emit('sortStart', params);
      }),
    ];

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