import { FiltersMap, Tags } from '@wix/bex-core';
import { cairoAssignTagsToEntities, cairoUndoClicked } from '@wix/bex-core/bi';

import {
  TagsBulkAssignPopoverState,
  TagsBulkAssignPopoverStateParams,
} from './TagsBulkAssignPopoverState';

export interface BulkUpdateTagsParams {
  entityIds: string[];
  assignTags: Tags;
  unassignTags: Tags;
}

export interface TagsBulkAssignSyncStateParams<T, F extends FiltersMap>
  extends Pick<
    TagsBulkAssignPopoverStateParams<T, F>,
    'bulkUpdateTags' | 'collectionState' | 'getTags' | 'bulkUpdateTagsShowUndoToast'
  > {
  container: TagsBulkAssignPopoverState<T, F>['container'];
  optimisticActions: TagsBulkAssignPopoverState<T, F>['optimisticActions'];
  tagsBIReporter: TagsBulkAssignPopoverState<T, F>['tagsBIReporter'];
}
export const MAX_SELECTED_ENTITIES_FOR_BULK_ASSIGN_SYNC = 100;

export class TagsBulkAssignSyncState<T, F extends FiltersMap> {
  readonly collectionState;
  readonly optimisticActions;
  readonly container;
  readonly tagsBIReporter;
  readonly bulkUpdateTags;
  readonly getTags;
  readonly showUndoToast: boolean;

  constructor(params: TagsBulkAssignSyncStateParams<T, F>) {
    const {
      collectionState,
      optimisticActions,
      container,
      tagsBIReporter,
      getTags,
      bulkUpdateTags,
      bulkUpdateTagsShowUndoToast: showUndoToast = true,
    } = params;
    this.collectionState = collectionState;
    this.optimisticActions = optimisticActions;
    this.container = container;
    this.tagsBIReporter = tagsBIReporter;
    this.getTags = getTags;
    this.bulkUpdateTags = bulkUpdateTags;
    this.showUndoToast = showUndoToast;
  }

  get canUseBulkSync() {
    const { allSelectedItemsFetched, selectedCountOrTotal } =
      this.collectionState.bulkSelect;

    return (
      allSelectedItemsFetched &&
      selectedCountOrTotal <= MAX_SELECTED_ENTITIES_FOR_BULK_ASSIGN_SYNC
    );
  }

  async applyChanges({
    newAssignedTagIds,
    newUnassignedTagIds,
    searchValue,
  }: {
    newAssignedTagIds: string[];
    newUnassignedTagIds: string[];
    searchValue: string;
  }) {
    this._reportApplyBi({
      newAssignedTagIds,
      newUnassignedTagIds,
      searchValue,
    });
    const items = this._calcOptimisticItems({
      newAssignedTagIds,
      newUnassignedTagIds,
    });
    const entityIds = this.collectionState.bulkSelect.selectedIds;

    this.optimisticActions.updateMany(items, {
      keepPosition: true,
      submit: async () => {
        return this.bulkUpdateTags({
          entityIds,
          assignTags: { privateTags: { tagIds: newAssignedTagIds } },
          unassignTags: { privateTags: { tagIds: newUnassignedTagIds } },
        });
      },
      successToast: {
        biName: 'cairo-update-bulk-assign-tags-success',
        message: this.container.translate(
          'cairo.tags.applyChanges.toast.success',
        ),
      },
      showUndoToast: this.showUndoToast,
      errorToast: () => ({
        biName: 'cairo-update-bulk-assign-tags-error',
        message: this.container.translate(
          'cairo.tags.collectionPage.toast.update.error',
        ),
      }),
      onUndo: this._reportUndoBi,
    });
  }

  private _calculateUpdatedTagIds({
    item,
    getTags,
    newAssignedTagIds,
    newUnassignedTagIds,
  }: {
    item: T;
    getTags?: (item: T) => Tags;
    newAssignedTagIds: string[];
    newUnassignedTagIds: string[];
  }) {
    const tags: Tags = (item as any).tags ?? getTags?.(item);

    const currentTagIds = tags?.privateTags?.tagIds || [];

    const updatedTagIds = Array.from(
      new Set(
        [...currentTagIds, ...newAssignedTagIds].filter(
          (tagId) => !newUnassignedTagIds.includes(tagId),
        ),
      ),
    );
    return updatedTagIds;
  }

  private _calcOptimisticItems({
    newAssignedTagIds,
    newUnassignedTagIds,
  }: {
    newAssignedTagIds: string[];
    newUnassignedTagIds: string[];
  }) {
    return this.collectionState.result.items.map((item: T) => {
      const selectedIds = this.collectionState.bulkSelect.selectedIds;
      const itemKey = this.collectionState.itemKey(item);
      if (!selectedIds.includes(itemKey)) {
        return item;
      }

      const updatedTagIds = this._calculateUpdatedTagIds({
        newAssignedTagIds,
        newUnassignedTagIds,
        item,
        getTags: this.getTags,
      });

      return { ...item, tags: { privateTags: { tagIds: updatedTagIds } } };
    });
  }

  private _reportApplyBi({
    newAssignedTagIds,
    newUnassignedTagIds,
    searchValue,
  }: {
    newAssignedTagIds: string[];
    newUnassignedTagIds: string[];
    searchValue: string;
  }) {
    let actionName = 'Both';
    if (newAssignedTagIds.length === 0) {
      actionName = 'Remove';
    } else if (newUnassignedTagIds.length === 0) {
      actionName = 'Assign';
    }

    const { selectedIds: selectedItemsIds } = this.collectionState.bulkSelect;

    this.tagsBIReporter.reportBi(
      cairoAssignTagsToEntities({
        actionName,
        isFromSearch: searchValue !== '',
        itemIds: JSON.stringify(selectedItemsIds),
        numItems: selectedItemsIds.length,
        assignedTags: JSON.stringify(newAssignedTagIds),
        unAssignedTags: JSON.stringify(newUnassignedTagIds),
        origin: 'Assign Tags Modal',
      }),
    );
  }

  private _reportUndoBi = () => {
    this.tagsBIReporter.reportBi(
      cairoUndoClicked({
        actionName: 'Save',
        changedAttributes: 'Tags Assignment',
      }),
    );
  };
}
