import {
  FiltersMap,
  WixPatternsContainer,
  DataResultRaw,
  CollectionState,
  CollectionStateParams,
  stringFilter,
  CollectionOptimisticActions,
  QueryState,
} from '@wix/bex-core';
import {
  action,
  computed,
  makeObservable,
  observable,
  runInAction,
} from 'mobx';

export interface KanbanStageStateBaseParams<T> {
  readonly container: WixPatternsContainer;
  readonly initialPage?: DataResultRaw<T>;
  readonly name: string;
  readonly key: string;
  readonly kanbanId: string;
}

export interface KanbanStageStateParams<
  T,
  S,
  F extends FiltersMap & { stageId: ReturnType<typeof stringFilter> },
> extends KanbanStageStateBaseParams<T> {
  readonly fetchData: CollectionStateParams<T, F>['fetchData'];
  readonly itemKey: CollectionStateParams<T, F>['itemKey'];
  readonly itemName: CollectionStateParams<T, F>['itemName'];
  readonly stage: S;
  readonly query: QueryState<F>;
}

export class KanbanStageState<
  T,
  S,
  F extends FiltersMap & { stageId: ReturnType<typeof stringFilter> },
> {
  readonly container: WixPatternsContainer;
  readonly itemsCollectionState: CollectionState<T, F>;
  name: string;
  readonly key: string;
  readonly kanbanId: string;
  readonly stage: S;
  _total?: number;
  isRefetching = false;
  private optimisticActions: CollectionOptimisticActions<T, F>;

  constructor(params: KanbanStageStateParams<T, S, F>) {
    this.container = params.container;
    this.name = params.name;
    this.key = params.key;
    this.kanbanId = params.kanbanId;
    this.stage = params.stage;
    this._total = params.initialPage?.total ?? undefined;
    this.itemsCollectionState = new CollectionState<T, F>({
      errorMonitor: this.container.errorMonitor,
      history: this.container.history,
      lodash: this.container.lodash,
      onlineState: this.container.onlineState,
      queryCache: this.container.queryCache,
      translate: this.container.translate,
      queryName: `kanban-items-${this.kanbanId}-${this.key}`,
      fetchData: params.fetchData,
      itemKey: params.itemKey,
      itemName: params.itemName,
      filters: {
        ...params.query.filters,
        stageId: stringFilter({ initialValue: this.key }),
      },
      initialPage: params.initialPage,
      errorHandler: this.container.errorHandler,
      paginationMode: 'cursor',
      search: params.query.search,
    });

    this.optimisticActions = new CollectionOptimisticActions({
      collection: this.itemsCollectionState,
      container: this.container,
    });

    makeObservable(this, {
      name: observable,
      _total: observable,
      fetchMore: action,
      stageName: computed,
      stageKey: computed,
      isRefetching: observable,
      hasError: computed,
      setTotal: action,
      isLoading: computed,
      totalItems: computed,
    });
  }

  init() {
    const disposers = [
      this.itemsCollectionState.init({ skipFilterListeners: true }),
      this.optimisticActions.init(),
    ];

    return () => {
      disposers.forEach((disposer) => disposer?.());
    };
  }

  get hasError() {
    return this.itemsCollectionState.result.status?.isError;
  }

  items() {
    return this.itemsCollectionState.keyedItems.map((item: any) => item.item);
  }

  itemName(item: T) {
    return this.itemsCollectionState.itemName(item);
  }

  itemKey(item: T) {
    return this.itemsCollectionState.itemKey(item);
  }

  get totalItems() {
    return this._total;
  }

  setTotal(total: number) {
    this._total = total;
  }

  get stageName() {
    return this.name;
  }

  get stageKey() {
    return this.key;
  }

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

  fetchMore() {
    this.itemsCollectionState.fetchNextPageIfNeeded();
  }

  isFetchingMore() {
    return true;
  }

  deleteItem(item: T) {
    this.itemsCollectionState.optimisticActions.deleteOne(item, {
      submit: () => Promise.resolve(),
    });
    runInAction(() => {
      this.setTotal((this._total ?? 0) - 1);
    });
  }

  createItem(item: T) {
    this.itemsCollectionState.optimisticActions.createOne(item, {
      submit: () => Promise.resolve(),
    });
    runInAction(() => {
      this.setTotal((this._total ?? 0) + 1);
    });
  }

  showErrorToast(error: unknown) {
    const resolvedError =
      this.container.errorHandler?.getResolvedError?.(error);
    resolvedError && this.container.errorHandler?.showError?.(resolvedError);
  }

  updateItem(updatedItem: T) {
    const allItems = this.itemsCollectionState.result.keyedItems.map(
      (k) => k.item,
    );
    const updatedItems = allItems.map((item) =>
      this.itemKey(item) === this.itemKey(updatedItem) ? updatedItem : item,
    );
    this.itemsCollectionState.optimisticActions.updateMany(updatedItems, {
      submit: () => Promise.resolve(updatedItems),
      keepPosition: true,
    });
  }

  async refetchStage() {
    runInAction(() => {
      this.isRefetching = true;
    });
    try {
      await this.itemsCollectionState.refreshAllPages();
    } finally {
      runInAction(() => {
        this.isRefetching = false;
      });
    }
  }
}
