import { action, computed, makeObservable, observable } from 'mobx';
import {
  CollectionOptimisticActions,
  CollectionState,
  ConditionalModalState,
  FiltersMap,
  SortOrder,
  WixPatternsContainer,
} from '@wix/bex-core';
import type { MessageModalLayoutProps } from '@wix/design-system';
import type { KeyedItem } from '@wix/bex-core';
import { ToolbarCollectionState } from '../ToolbarCollectionState';
import { TableState } from '../TableState';
import { ICollectionComponentState } from '../ICollectionComponentState';
import { CellInteractionState } from './CellInteractionState';
import { CellValueState } from './CellValueState';
import { ClipboardState } from './ClipboardState';
import { MutationState } from './MutationState';
import { FieldActionsState } from '../FieldActionsState';
import type { EditableTableMutationHandlers } from './MutationState';
import type { Cell, CellType, EditableColumn, ValidationResult } from './types';
import type { FieldActions } from '../../types/SchemaConfig';
import type { FieldActionPermissions } from '../../components/FieldActions';

export type EditableTableFieldActionsPermissions = FieldActionPermissions;

export interface EditableTablePermissions {
  addItemDisabled?: boolean;
  updateItemDisabled?: boolean;
  removeItemDisabled?: boolean;
  fieldActions?: EditableTableFieldActionsPermissions;
}

export interface EditableTableStateParams<T, F extends FiltersMap> {
  readonly collection: CollectionState<T, F>;
  readonly container: WixPatternsContainer;
  readonly toolbar?: ToolbarCollectionState<T, F>;
  readonly permissions?: EditableTablePermissions;
  readonly mutations: EditableTableMutationHandlers<T>;
  readonly fieldActions?: FieldActions;
  readonly refresh?: () => Promise<void>;
  readonly createOptimisticActions: (
    collection: CollectionState<T, F>,
  ) => CollectionOptimisticActions<T, F>;
  /**
   * Additional cell types to register alongside the built-in ones.
   * Augment `CellTypeConfigMap` to use these types on `EditableColumn.cellType`.
   */
  readonly extraCellTypes?: CellType[];
}

export interface EditableTableStatePublicAPI<T, F extends FiltersMap> {
  /** Underlying CollectionState instance */
  readonly collection: CollectionState<T, F>;
  /** Underlying ToolbarCollectionState instance */
  readonly toolbar: ToolbarCollectionState<T, F>;
  /** Reset active filters & sorting and adds new items to the collection */
  onAddItemActionComplete(items: T[]): void;
  /** Sort by column */
  sort: (
    columnId: string,
    params?: { forceDirection?: SortOrder; multiple?: boolean },
  ) => void;
}

export class EditableTableState<T, F extends FiltersMap>
  implements ICollectionComponentState, EditableTableStatePublicAPI<T, F>
{
  readonly toolbar: ToolbarCollectionState<T, F>;
  readonly tableState: TableState<T, F>;

  readonly cellInteraction: CellInteractionState<T, F>;
  readonly cellValue: CellValueState<T, F>;
  readonly clipboard: ClipboardState<T, F>;
  readonly mutation: MutationState<T, F>;
  readonly deleteConfirmModal = new ConditionalModalState<
    Partial<MessageModalLayoutProps>
  >();
  readonly fieldActions: FieldActionsState;

  addItemDisabled = false;
  updateItemDisabled = false;
  removeItemDisabled = false;

  _allColumns: EditableColumn<T>[] = [];

  constructor(params: EditableTableStateParams<T, F>) {
    this.toolbar =
      params.toolbar ??
      new ToolbarCollectionState({
        collection: params.collection,
        container: params.container,
        componentType: 'EditableTable',
      });

    this.tableState = new TableState({
      collection: params.collection,
      container: params.container,
      toolbar: this.toolbar,
    });

    this.cellValue = new CellValueState<T, F>(
      this,
      new Map(params.extraCellTypes?.map((ct) => [ct.type, ct])),
    );

    this.cellInteraction = new CellInteractionState<T, F>(
      (editingValue, cell) => {
        this.cellValue.commitHandler(editingValue, cell);
      },
      this,
    );
    this.clipboard = new ClipboardState<T, F>(this);
    this.mutation = new MutationState<T, F>(
      this,
      params.mutations,
      params.createOptimisticActions,
    );
    this.fieldActions = new FieldActionsState(
      params.collection.translate,
      params.fieldActions,
      params.permissions?.fieldActions,
      params.collection.showToast,
      params.refresh,
    );

    makeObservable(this, {
      addItemDisabled: observable,
      updateItemDisabled: observable,
      removeItemDisabled: observable,
      _allColumns: observable.ref,
      editableColumns: computed,
      _columnMap: computed,
      init: action,
      confirmDeleteItems: action,
    });

    if (params.permissions) {
      this.addItemDisabled = params.permissions.addItemDisabled ?? false;
      this.updateItemDisabled = params.permissions.updateItemDisabled ?? false;
      this.removeItemDisabled = params.permissions.removeItemDisabled ?? false;
    }
  }

  get collection() {
    return this.tableState.collection;
  }

  get keyedItems() {
    return this.tableState.keyedItems;
  }

  get visibleColumns() {
    return this.tableState.visibleColumns;
  }

  get itemsToRenderCount() {
    return this.tableState.itemsToRenderCount;
  }

  get _columnMap(): Map<string, EditableColumn<T>> {
    return new Map(this._allColumns.map((c) => [c.id, c]));
  }

  _getKeyedItem(key: string): KeyedItem<T> | undefined | null {
    return this.collection.getKeyedItem(key);
  }

  _toIndices(pos: Cell): { row: number; col: number } | null {
    const row = this.keyedItems.findIndex((ki) => ki.key === pos.rowKey);
    const col = this.editableColumns.findIndex((c) => c.id === pos.columnId);
    return row === -1 || col === -1 ? null : { row, col };
  }

  _toPosition(row: number, col: number): Cell | null {
    const ki = this.keyedItems[row];
    const c = this.editableColumns[col];
    return ki && c ? { rowKey: ki.key, columnId: c.id } : null;
  }

  onAddItemActionComplete = (items: T[]) => {
    return this.tableState.onAddItemActionComplete(items);
  };

  onAddItemClick = (origin?: string) => {
    this.toolbar.toolbarBIReporter.onAddItemClick(origin);
  };

  get editableColumns(): EditableColumn<T>[] {
    const visibleIds = this.visibleColumns.map((c) => c.id);
    if (!visibleIds.length) {
      return this._allColumns;
    }
    return visibleIds
      .map((id) => this._allColumns.find((c) => c.id === id))
      .filter((c): c is EditableColumn<T> => c !== undefined);
  }

  init(columns: EditableColumn<T>[]) {
    this._allColumns = columns;
    this.cellValue._buildCellTypeMap();
    return this.mutation.init();
  }

  addItem(afterItemKey?: string): string {
    return this.mutation.addItem(afterItemKey);
  }

  duplicateItem(rowKey: string) {
    this.mutation.duplicateItem(rowKey);
  }

  deleteItems(rowKeys: string[]) {
    this.mutation.deleteItems(rowKeys);
  }

  confirmDeleteItems(rowKeys: string[]) {
    const { translate: t } = this.collection;
    this.deleteConfirmModal.open({
      theme: 'destructive',
      title: t('cairo.deleteModal.title'),
      content: t('cairo.deleteModal.body'),
      primaryButtonText: t('cairo.delete.button'),
      secondaryButtonText: t('cairo.cancel.button'),
      primaryButtonOnClick: () => {
        this.deleteItems(rowKeys);
      },
    });
  }

  resolveCellTypeByColumnId(columnId: string) {
    return this.cellValue.resolveCellTypeByColumnId(columnId);
  }

  getCellDisplayValue(rowKey: string, columnId: string): any {
    return this.cellValue.getCellDisplayValue(rowKey, columnId);
  }

  getCellValidation(rowKey: string, columnId: string): ValidationResult {
    return this.cellValue.getCellValidation(rowKey, columnId);
  }

  isCellEditable(rowKey: string, columnId: string): boolean {
    return this.cellValue.isCellEditable(rowKey, columnId);
  }

  clearCells(cells: Cell[]) {
    this.cellValue.clearCells(cells);
  }

  handleCellClick(rowKey: string, columnId: string, shiftKey: boolean) {
    if (this.cellInteraction.isEditing) {
      if (this.cellInteraction.isCellEditing(rowKey, columnId)) {
        // Click landed inside the active editor — let the editor handle it.
        return;
      }
      // Shift-click while editing: ignore — extending a range selection during
      // an active edit is not a meaningful action.
      if (shiftKey) {
        return;
      }
      // Plain click on a different cell: commit the edit and move focus.
      this.cellInteraction.commitEdit();

      // If the target cell wants edit-on-click, enter edit directly.
      const col = this._columnMap.get(columnId);
      const res = col ? this.resolveCellTypeByColumnId(col.id) : null;
      if (res?.editOnClick) {
        const val = this.getCellDisplayValue(rowKey, columnId);
        if (res.editOnClick(val) && this.isCellEditable(rowKey, columnId)) {
          this.cellInteraction.focusCell(rowKey, columnId);
          this.cellInteraction.startEdit(rowKey, columnId, 'click');
          return;
        }
      }

      this.cellInteraction.focusCell(rowKey, columnId);
      return;
    }

    const column = this._columnMap.get(columnId);
    const resolved = column ? this.resolveCellTypeByColumnId(column.id) : null;
    if (resolved?.toggleValue || resolved?.showEditWhenFocused) {
      // Toggle/showEditWhenFocused cells: click focuses the cell and shows the
      // EditComponent affordance. Keyboard navigation remains active.
      this.cellInteraction.focusCell(rowKey, columnId);
      return;
    }

    if (resolved?.editOnClick) {
      const value = this.getCellDisplayValue(rowKey, columnId);
      if (
        resolved.editOnClick(value) &&
        this.isCellEditable(rowKey, columnId)
      ) {
        this.cellInteraction.focusCell(rowKey, columnId);
        this.cellInteraction.startEdit(rowKey, columnId, 'click');
        return;
      }
    }

    if (shiftKey && this.cellInteraction.selectionAnchor) {
      this.cellInteraction.selectRange(this.cellInteraction.selectionAnchor, {
        rowKey,
        columnId,
      });
    } else {
      this.cellInteraction.focusCell(rowKey, columnId);
    }
  }

  handleCellDoubleClick(rowKey: string, columnId: string) {
    if (this.updateItemDisabled) {
      return;
    }
    const column = this._columnMap.get(columnId);
    const resolved = column ? this.resolveCellTypeByColumnId(column.id) : null;
    // Toggle-value cells (e.g. boolean) activate on single click, not double-click.
    // Entering edit mode on double-click would apply the editing box-shadow
    // without any EditComponent being shown, causing a visual border glitch.
    if (resolved?.toggleValue || resolved?.showEditWhenFocused) {
      return;
    }
    if (this.isCellEditable(rowKey, columnId)) {
      this.cellInteraction.startEdit(rowKey, columnId, 'doubleClick');
    }
  }

  sort = (
    columnId: string,
    {
      forceDirection,
      multiple,
    }: { forceDirection?: SortOrder; multiple?: boolean } = {},
  ) => {
    this.collection.sort(
      { id: columnId },
      { forceDirection, clearResult: true, multiple },
    );
  };
}
