import {
  BulkSelectKeyedItem,
  CollectionState,
  FiltersMap,
  Fqdn,
  PickerEvents,
  PickerState,
  ReportBI,
  SelectionEntry,
  selectionStatusWixPatternsPreset,
  TaskState,
  FilterEventOptions,
  withoutDefaults,
  Filter,
} from '@wix/bex-core';
import { addEventListener } from '@wix/bex-core/util';
import type { PickerModalState } from '../hooks';
import { UsePickerContentParamsBase } from '../hooks/usePickerContent';
import { fromMultipleSelectionToBoolean } from '../components/MaxSelection';
import { action, makeObservable, observable, runInAction } from 'mobx';
import { cairoFilterToggled, newItemCreationStart } from '@wix/bex-core/bi';
import { EventEmitter } from 'events';
import { TypedEmitter } from '@wix/bex-core/events';
import { PickerContentStateBI } from './PickerContentStateBI';
import { AdditionalStepState } from './AdditionalStepState';

const isPickerSelectionV2 = <T, F extends FiltersMap>(
  e: UsePickerContentParamsBase<T, F>['initialSelect'],
) =>
  e != null && typeof e === 'object' && ('defaultStatus' in e || 'values' in e);

export interface PickerContentStateParams<T, F extends FiltersMap> {
  modalState: PickerModalState<T, F>;
  collection: CollectionState<T, F>;
  createBILogger: <P extends Fqdn>(defaultParams: P) => ReportBI;
  events?: PickerEvents<T, F>;
  initialSelect?: UsePickerContentParamsBase<T, F>['initialSelect'];
}

export class PickerContentState<T, F extends FiltersMap> {
  readonly picker: PickerState<T, F>;
  readonly modalState: PickerModalState<T, F>;
  readonly initTask = new TaskState();
  readonly reportBi;

  readonly events = new EventEmitter() as TypedEmitter<{
    beforeToggleIsSelectAll: () => {
      afterToggleIsSelectAll: () => unknown;
    };
  }>;

  initialSelect: PickerContentStateParams<T, F>['initialSelect'];

  additionalStep?: AdditionalStepState<T, F>;

  isAdditionalStepShown = false;

  autoAdditionalStepOnInitialSelect = false;
  isAutoAdditionalStepPending = true;

  constructor(params: PickerContentStateParams<T, F>) {
    const { collection, modalState, createBILogger, events, initialSelect } =
      params;

    this.initialSelect = initialSelect;

    this.modalState = modalState;

    this.reportBi = createBILogger({
      ...collection.fqdn,
      type: 'modal',
      componentType: 'Picker Modal',
      artifactId: modalState.environment.artifactId,
    });

    this.picker = new PickerState({
      collection,
      getMaxItems: modalState.getMaxItems,
      shouldFilterNotAffectTotalCount:
        modalState.shouldFilterNotAffectTotalCount,
      minContentHeight: modalState.minContentHeight,
      events,
      reportBi: this.reportBi,
    });

    makeObservable(this, {
      _setInitialSelection: action,
      isAdditionalStepShown: observable.ref,
      setIsAdditionalStepShown: action.bound,
      isAutoAdditionalStepPending: observable.ref,
    });

    if (modalState.pickerContentRef.current == null) {
      this._setInitialData();
    }

    this._setInitialSelection();
  }

  _setInitialData() {
    const { modalState, picker } = this;
    const { collection } = picker;
    const { firstPageResult } =
      modalState.fetchFirstPage.status.value.data ?? {};

    if (firstPageResult) {
      collection.setInitialPage(firstPageResult);
    }
  }

  _setInitialSelection() {
    const { initialSelect, picker } = this;
    const { collection } = picker;
    const {
      bulkSelect,
      result: { keyedItems, itemKey },
    } = collection;

    if (initialSelect == null) {
      return;
    }

    if (typeof initialSelect === 'boolean') {
      const [firstItem] = keyedItems;
      if (firstItem) {
        bulkSelect.select.forceSet(firstItem);
      }
      return;
    }

    if (typeof initialSelect === 'function') {
      const initialSelectItems = keyedItems.filter(({ item }, index) =>
        initialSelect(item, index),
      );

      bulkSelect.select.syncValues(
        initialSelectItems,
        bulkSelect.select.finalStatus,
      );
      return;
    }

    if (isPickerSelectionV2(initialSelect)) {
      const { defaultStatus, values } = initialSelect;
      if (defaultStatus) {
        bulkSelect.select.defaultStatus =
          typeof defaultStatus === 'object'
            ? defaultStatus
            : selectionStatusWixPatternsPreset[defaultStatus];
      }

      if (values) {
        bulkSelect.select.forceSetMany(
          values.map(({ status, value }) => ({
            value: {
              item: value,
              key: itemKey(value),
            },
            status:
              typeof status === 'object'
                ? status
                : selectionStatusWixPatternsPreset[status],
          })),
        );
      }

      return;
    }

    if (initialSelect.isSelectAll) {
      bulkSelect.selectAll();

      if (initialSelect.uncheckedValues) {
        initialSelect.uncheckedValues.forEach((item) => {
          typeof item === 'string'
            ? bulkSelect.deselectOne(item)
            : bulkSelect.deselectOneItem({
                key: itemKey(item),
                item,
              });
        });
      }
      return;
    }

    if (initialSelect.selectedValues) {
      initialSelect.selectedValues.forEach((item) => {
        typeof item === 'string'
          ? bulkSelect.selectOne(item)
          : bulkSelect.selectOneItem({
              key: itemKey(item),
              item,
            });
      });
    }

    if (initialSelect.partiallySelectedIds) {
      const resolvedEntriesFromCollection =
        initialSelect.partiallySelectedIds.map((item) => {
          const collectionItem =
            typeof item === 'string'
              ? bulkSelect.getCollectionItem(item)
              : {
                  key: itemKey(item),
                  item,
                };

          if (!collectionItem) {
            return null;
          }

          return {
            status: selectionStatusWixPatternsPreset.indeterminate,
            value: {
              key: collectionItem.key,
              item: collectionItem.item,
            },
          };
        });
      bulkSelect.select.forceSetMany(
        resolvedEntriesFromCollection.filter(
          (e): e is SelectionEntry<BulkSelectKeyedItem<T>> => e != null,
        ),
      );
    }
  }

  selectCreatedItems(items?: T[]) {
    const {
      picker,
      modalState: { multipleRef },
    } = this;
    const {
      collection: { bulkSelect, itemKey },
    } = picker;

    if (items?.length) {
      const [item] = items;
      const multiple = fromMultipleSelectionToBoolean(multipleRef.current);
      if (!multiple) {
        bulkSelect.resetToDefaults();
      }
      bulkSelect.selectOne(itemKey(item));
    }
  }

  onCreateNew = async (items?: T[]) => {
    await this.picker.onCreateNew(items);
    this.selectCreatedItems(items);
  };

  onCreateNewButtonClick = (
    onClick?: (param: { onClose: (arr?: T[]) => void }) => void,
  ): void => {
    const {
      modalState: { _isCustomContentVisible },
      picker,
    } = this;

    onClick?.({ onClose: this.onCreateNew });
    _isCustomContentVisible.value = true;
    this.reportBi(
      newItemCreationStart({
        ...picker._commonDynamicBiParams(),
        numSelectedItems: picker.collection.bulkSelect.select.selectedCount,
      }),
    );
  };

  _resultUpdated = () => {
    const { initialSelect, picker } = this;
    const { collection } = picker;
    const {
      bulkSelect,
      result: { keyedItems },
    } = collection;

    if (typeof initialSelect === 'function') {
      const initialSelectItems = keyedItems.filter(({ item }, index) =>
        initialSelect(item, index),
      );

      bulkSelect.select.syncValues(
        initialSelectItems,
        bulkSelect.select.finalStatus,
      );
      return;
    }
  };
  _reportFilterToggleBiOnNextResult(
    params: FilterEventOptions | undefined,
    filter: Filter<unknown>,
  ) {
    const { actionType, clickedValueKey } = params ?? {};
    const {
      picker: { collection },
    } = this;

    const onFetch = () => {
      collection.emitter.off('fetch', onFetch);
      return action(() => {
        this.reportBi(
          withoutDefaults(cairoFilterToggled)({
            ...this.picker._commonDynamicBiParams(),
            origin: 'Picker Modal',
            filterValue: clickedValueKey,
            filterName: filter.name,
            actionType,
            numFiltersActive: collection.query.activeFiltersCount,
            filteredListSize: collection.result.total,
            isCustomField: !!filter.isCustomField,
          }),
        );
      });
    };

    this.picker.collection.emitter.on('fetch', onFetch);
  }

  init(initParams?: { autoAdditionalStepOnInitialSelect?: boolean }) {
    const { picker, modalState, initTask } = this;
    const { collection } = picker;

    if (initParams?.autoAdditionalStepOnInitialSelect) {
      this.autoAdditionalStepOnInitialSelect = true;
    } else {
      this.isAutoAdditionalStepPending = false;
    }

    const bi = new PickerContentStateBI({ state: this });

    const disposers = [picker.init(), bi.init()];

    this.picker.collection.query.filtersArray.forEach((filter) => {
      disposers.push(
        addEventListener(
          filter.events as TypedEmitter<{
            change: (opt: FilterEventOptions | undefined) => unknown;
          }>,
          'change',
          (params: FilterEventOptions | undefined) => {
            this._reportFilterToggleBiOnNextResult(params, filter);
          },
        ),
      );
    });
    collection.emitter.on('resultUpdated', this._resultUpdated);
    modalState.pickerContentRef.current = {
      onCreateNew: this.onCreateNew,
    };

    initTask.run(async () => {
      await picker.initTask.status.promise;
      if (!isPickerSelectionV2(this.initialSelect)) {
        this._setInitialSelection();
      }

      if (this.autoAdditionalStepOnInitialSelect) {
        runInAction(() => {
          const selectedItem =
            this.picker.collection.bulkSelect.selectedValues?.[0];
          if (selectedItem) {
            this.isAdditionalStepShown = true;
          }
          this.isAutoAdditionalStepPending = false;
        });
      }
    });

    return () => {
      const { current: pickerContent } = modalState.pickerContentRef;
      if (pickerContent) {
        pickerContent.onCreateNew = undefined;
      }
      disposers.forEach((d) => d());
      picker.collection.emitter.off('resultUpdated', this._resultUpdated);
    };
  }

  toggleIsSelectAll = action(() => {
    const { picker, events } = this;
    const {
      collection: {
        bulkSelect,
        query: { hasActiveFilters },
        result,
      },
    } = picker;

    const {
      allSelected,
      select: { isEmpty },
    } = bulkSelect;

    const afters = events.listeners('beforeToggleIsSelectAll').map((l) => l());

    if (allSelected || !isEmpty) {
      bulkSelect.toggleIsSelectAll();
    } else if (hasActiveFilters) {
      for (const keyedItem of result.keyedItems) {
        bulkSelect.selectOneItem(keyedItem);
      }
    } else {
      bulkSelect.toggleIsSelectAll();
    }

    afters.forEach(({ afterToggleIsSelectAll }) => afterToggleIsSelectAll());
  });

  setIsAdditionalStepShown(value: boolean) {
    this.isAdditionalStepShown = value;
  }
}
