import {
  CollectionState,
  Filter,
  FilterItem,
  FiltersMap,
  withoutDefaults,
} from '@wix/bex-core';
import { cairoFilterToggled } from '@wix/bex-core/bi';
import { action, computed, makeObservable } from 'mobx';
import { ToolbarCollectionState } from '../../state';
import { EventEmitter } from 'events';
import { TypedEmitter } from '@wix/bex-core/events';
import { addEventListener } from '@wix/bex-core/util';
import groupBy from 'lodash/groupBy';
import { FilterTagContent } from './FilterTagContent';
import React from 'react';
import { FilterProps } from '../../model';

export interface FilterTagValue<V> {
  id: string;
  filter: Filter<V>;
  value: FilterItem<V>;
}

export interface AppliedFiltersTagListStateParams<T, F extends FiltersMap> {
  readonly table: ToolbarCollectionState<T, F>;
}

export class AppliedFiltersTagListState<T, F extends FiltersMap> {
  readonly table;
  readonly events = new EventEmitter() as TypedEmitter<{
    fetch: (collection: CollectionState<unknown, {}>) => () => unknown;
  }>;

  maxWidth = '408px';
  constructor(params: Readonly<AppliedFiltersTagListStateParams<T, F>>) {
    this.table = params.table;

    makeObservable(this, {
      filterValues: computed,
      tags: computed,
      filterPropsMap: computed,
    });
  }

  init() {
    const {
      multi: { collections },
    } = this.table;

    const disposers = [
      ...collections.map((collection) =>
        addEventListener(collection.emitter, 'fetch', () => {
          const ls = this.events.listeners('fetch').map((l) => l(collection));
          return () => {
            ls.forEach((l) => l());
          };
        }),
      ),
    ];

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

  get filterPropsMap() {
    const {
      table: { filtersProps },
    } = this;

    return new Map(
      filtersProps?.map((filterProps) => [filterProps.filter, filterProps]) ??
        [],
    );
  }

  toTag<V>(key: string, filter: Filter<V>) {
    const { filterPropsMap } = this;
    const { value: filterValue } = filter;

    const explodeTags = filterPropsMap.get(filter)?.explodeTags;

    return filter.toArray.map((value) => ({
      id:
        Array.isArray(filterValue) || explodeTags
          ? `${key}-${filter.itemKey(value)}`
          : key,
      filter,
      value,
    }));
  }

  get filterValues() {
    const {
      table: { activeFiltersEntries },
    } = this;

    return activeFiltersEntries
      .filter(([_, filter]) => !filter._hideFromToolbar)
      .flatMap(([key, filter]) => this.toTag(key, filter));
  }

  get tags() {
    const { filterValues, filterPropsMap } = this;
    const groupedFilters = groupBy(filterValues, 'filter.name');

    return Object.entries(groupedFilters).flatMap(([_, filterValuesGroup]) => {
      const filterProps = filterPropsMap.get(filterValuesGroup[0].filter);

      if (filterProps?.renderToolbarTag) {
        return this.generateNonAggregatedTag(filterValuesGroup, filterProps);
      }

      return this.generateAggregatedTag(filterValuesGroup, filterProps);
    });
  }

  _reportFilterToggleBiOnNextResult<V>(
    filter: Filter<V>,
    values: FilterItem<V>[],
  ) {
    const filterValuesKeys = values.map((value) => filter.itemKey(value));

    const { table, events } = this;
    const { reportBi } = table;

    const onFetch = (collection: CollectionState<unknown>) => {
      events.off('fetch', onFetch);

      return action(() => {
        reportBi(
          withoutDefaults(cairoFilterToggled)({
            ...table._commonDynamicBiParams(),
            ...collection._commonDynamicBiParams(),
            actionType: 'remove',
            origin: 'sub-toolbar',
            filterName: filter.name,
            filterValue: filterValuesKeys.join(),
            numFiltersActive: collection.query.activeFiltersCount,
            filteredListSize: collection.result.total,
            isCustomField: !!filter.isCustomField,
          }),
        );
      });
    };

    events.on('fetch', onFetch);
  }

  onClearButtonClick = action(() => {
    const { table } = this;
    const { collection } = table;
    const {
      query: { nonPersistentFilters },
    } = collection;
    collection.emitter.emit('beforeClearFilters', { origin: 'toolbar' });
    nonPersistentFilters
      .filter((filter) => !filter._hideFromToolbar)
      .forEach((filter) => {
        filter?.reset({ emitEvents: ['change', 'refresh'] });
      });
  });

  private generateNonAggregatedTag<V>(
    filterValuesGroup: FilterTagValue<V>[],
    filterProps?: FilterProps<V>,
  ) {
    return filterValuesGroup.map(({ id, filter, value }) => {
      return {
        children: filter.itemName(value),
        key: id,
        filterProps,
        id,
        dataHook: id,
        filter,
        values: [value],
        maxWidth: this.maxWidth,
        ...filterProps?.renderToolbarTag?.(value),
      };
    });
  }

  private generateAggregatedTag<V>(
    filterValuesGroup: FilterTagValue<V>[],
    filterProps?: FilterProps<V>,
  ) {
    return {
      children: (
        <FilterTagContent
          filterProps={filterProps}
          values={filterValuesGroup}
        />
      ),
      key: filterProps?.filter.name!,
      filterProps,
      id: filterProps?.filter.name!,
      dataHook: filterProps?.filter.name!,
      filter: filterValuesGroup[0].filter,
      values: filterValuesGroup.map(({ value }) => value),
      maxWidth: this.maxWidth,
    };
  }
}
