import { CollectionOptimisticActions, FiltersMap } from '@wix/bex-core';
import { arrayMove } from '@wix/wix-style-react-incubator/dnd-kit/sortable';
import { addEventListener } from '@wix/bex-core/util';
import type { CustomColumnsDragAndDrop } from '../components/CustomColumns/CustomColumnsDragAndDrop';
import { CollectionDragAndDropState } from '../components/DragAndDrop';
import { Column } from '../model';
import { action, computed, makeObservable } from 'mobx';
import { RectState } from './RectState';
import { indexOfCompareFn } from './indexOfCompareFn';
import { CustomColumnsPanelStateBase } from './CustomColumnsPanelStateBase';

export interface DragAndDropColumnsDataSource {
  readonly columns: Column[];
  readonly selectedColumns: {
    readonly value: { id: string; isSelected?: boolean }[];
    refresh(value: { id: string; isSelected?: boolean }[]): void;
  };
}

export interface CustomColumnsDragAndDropStateParams<T, F extends FiltersMap> {
  readonly customColumns: CustomColumnsPanelStateBase<T, F>;
  readonly dataSource: DragAndDropColumnsDataSource;
  readonly components: typeof CustomColumnsDragAndDrop;
  readonly modalsContainerRef: {
    current: HTMLElement | null | undefined;
  };
}

export class CustomColumnsDragAndDropState<T, F extends FiltersMap> {
  readonly container;
  readonly customColumns;
  readonly dataSource;
  readonly columnsActions;
  readonly components;

  get _dnd() {
    return this.collectionDragAndDropState?.dnd;
  }

  collectionDragAndDropState: CollectionDragAndDropState<Column, {}> | null =
    null;

  forceRenderIndexes: number[] | undefined = undefined;

  reportBi = () => {
    // report columns re-order BI here
  };

  readonly listRect;

  constructor(params: CustomColumnsDragAndDropStateParams<T, F>) {
    const { customColumns, dataSource, components } = params;
    this.components = components;
    this.container = customColumns.container;
    this.listRect = new RectState({
      container: customColumns.container,
    });
    this.customColumns = customColumns;
    this.dataSource = dataSource;
    this.columnsActions = new CollectionOptimisticActions({
      container: this.container,
      collection: this.customColumns.columnsCollection,
    });

    this.collectionDragAndDropState = new CollectionDragAndDropState({
      state: this,
      a11yContainer: params.modalsContainerRef,
      container: this.container,
    });

    makeObservable(this, {
      _onCustomColumnsReorder: action.bound,
      containerRect: computed,
      orderedColumns: computed,
      orderedColumnsByReorderDisabled: computed,
    });
  }

  get orderedColumnsByReorderDisabled() {
    return this.dataSource.columns.reduce(
      (state, column) => {
        column.reorderDisabled
          ? state.reorderDisabled.push(column)
          : state.regular.push(column);

        return state;
      },
      { regular: [] as Column[], reorderDisabled: [] as Column[] },
    );
  }

  get orderedColumns(): Column[] {
    const { dataSource } = this;
    const {
      selectedColumns: { value: selectedColumns },
    } = dataSource;

    const { reorderDisabled, regular } = this.orderedColumnsByReorderDisabled;

    return [
      ...reorderDisabled,
      ...regular.sort(
        indexOfCompareFn(selectedColumns as { id: string }[], (c) => c.id),
      ),
    ];
  }

  get collection() {
    return this.customColumns.columnsCollection;
  }

  get containerRect() {
    const { listRect } = this;
    if (listRect.rect.left) {
      return {
        ...listRect.rect,
      };
    }
    return undefined;
  }

  _onCustomColumnsReorder(columnId: string, overColumnId: string) {
    const {
      dataSource: { selectedColumns },
    } = this;

    const columnsArray = selectedColumns.value;

    const reorderedColumnsArray = arrayMove(
      columnsArray,
      columnsArray.findIndex((c) => c.id === columnId),
      columnsArray.findIndex((c) => c.id === overColumnId),
    );

    selectedColumns.refresh(reorderedColumnsArray);
  }

  init() {
    const { columnsActions } = this;
    const disposers = [
      columnsActions.init(),
      addEventListener(columnsActions.events, 'updated', async () => {
        const {
          customColumns: { columnsCollection },
        } = this;
        await columnsCollection.refreshAllPages();
      }),
    ];

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