import { DeleteTableRowAction } from '../actions/delete-table-row-action';
import { InsertTableRowIntoDifferentSectionAction } from '../actions/insert-table-row-into-different-section-action';
import { InsertTableRowDirection, InsertTableRowIntoSameSectionAction, } from '../actions/insert-table-row-into-same-section-action';
import { ToggleHeaderRowAction } from '../actions/toggle-header-row-action';
import { RTETableBlock } from '../dto/table-block';
import { RTETableBodyBlock } from '../dto/table-body-block';
import { RTETableHeadBlock } from '../dto/table-head-block';
import { RTETableRowBlock } from '../dto/table-row-block';
import { getParentBlockByType } from '../utils/utils';
import { SettingEventHandler, TableSettingType } from './setting-event-handler.abstract';
export var RowOperationType;
(function (RowOperationType) {
  RowOperationType["HEADER_ROW"] = "HEADER_ROW";
  RowOperationType["ADD_ROW_ABOVE"] = "ADD_ROW_ABOVE";
  RowOperationType["ADD_ROW_BELOW"] = "ADD_ROW_BELOW";
  RowOperationType["DELETE_ROW"] = "DELETE_ROW";
  RowOperationType["SELECT_ROW"] = "SELECT_ROW";
})(RowOperationType || (RowOperationType = {}));
/**
 * Event handler for row operations in a table.
 */
export class RowOperationEventHandler extends SettingEventHandler {
  /**
   * @inheritDoc
   */
  canHandle(action) {
    return action[0] === TableSettingType.ROW;
  }
  /**
   * @inheritDoc
   */
  execute(action, extensionContext) {
    const operation = action[1];
    switch (operation) {
      case RowOperationType.ADD_ROW_ABOVE:
      case RowOperationType.ADD_ROW_BELOW:
        this.handleInsertRow(operation, extensionContext);
        break;
      case RowOperationType.DELETE_ROW:
        this.handleDeleteRow(extensionContext);
        break;
      case RowOperationType.HEADER_ROW:
        this.handleToggleHeader(extensionContext);
        break;
      case RowOperationType.SELECT_ROW:
        this.handleSelectRow(extensionContext);
        break;
      default:
        break;
    }
  }
  /**
   * Adds a new row to the table at the specified position.
   *
   * @param {RowOperationType} direction - The position to insert the new row (above or below).
   * @param {RTETableExtensionContext} extensionContext - The extension context.
   */
  handleInsertRow(direction, extensionContext) {
    const selectedRows = this.getAllSelectedRows(extensionContext);
    if (selectedRows.length > 0) {
      const insertDirection = direction === RowOperationType.ADD_ROW_ABOVE
        ? InsertTableRowDirection.ABOVE
        : InsertTableRowDirection.BELOW;
      // The reference block will control where to insert the new row
      const refBlock = insertDirection === InsertTableRowDirection.ABOVE
        ? selectedRows[0]
        : selectedRows[selectedRows.length - 1];
      const refBlockIsBodyRow = refBlock.parent instanceof RTETableBodyBlock;
      const action = this.shouldInsertHeaderRow(refBlock, insertDirection) || refBlockIsBodyRow
        ? new InsertTableRowIntoSameSectionAction(insertDirection, selectedRows)
        : new InsertTableRowIntoDifferentSectionAction(insertDirection, selectedRows);
      this.applyActionToTable(selectedRows[0], action);
    }
  }
  /**
   * Checks if a header row should be inserted based on the selected rows and position.
   *
   * @param {RTETableRowBlock} refBlock - The reference table row block.
   * @param {InsertTableRowDirection} direction - The direction to insert the new row (above or below).
   * @returns {boolean} True if a header row should be inserted, false otherwise.
   */
  shouldInsertHeaderRow(refBlock, direction) {
    if (direction === InsertTableRowDirection.ABOVE) {
      return refBlock.parent instanceof RTETableHeadBlock;
    }
    else {
      const parentBlock = refBlock.parent;
      return (parentBlock instanceof RTETableHeadBlock &&
        parentBlock.children[parentBlock.children.length - 1] !== refBlock);
    }
  }
  /**
   * Deletes the selected row(s) from the table.
   *
   * @param {RTETableExtensionContext} extensionContext - The extension context.
   */
  handleDeleteRow(extensionContext) {
    const selectedRows = this.getAllSelectedRows(extensionContext);
    if (selectedRows.length) {
      const action = new DeleteTableRowAction(selectedRows);
      this.applyActionToTable(selectedRows[0], action);
    }
  }
  /**
   * Converts selected rows to header rows or reverts them back to normal rows.
   *
   * @param {RTETableExtensionContext} extensionContext - The extension context.
   */
  handleToggleHeader(extensionContext) {
    const selectedRows = this.getAllSelectedRows(extensionContext);
    if (selectedRows.length > 0) {
      const action = new ToggleHeaderRowAction(selectedRows);
      this.applyActionToTable(selectedRows[0], action);
    }
  }
  /**
   * Selects the focus row(s) in the table.
   *
   * @param {RTETableExtensionContext} extensionContext - The extension context.
   */
  handleSelectRow(extensionContext) {
    const selectedRows = this.getAllSelectedRows(extensionContext);
    if (selectedRows.length > 0 && extensionContext && extensionContext.tableSelection) {
      const allCells = selectedRows.flatMap((row) => row.children);
      extensionContext.tableSelection.setSelectedCells(allCells);
    }
  }
  /**
   * Get all selected table rows.
   *
   * @param {RTETableExtensionContext} extensionContext - The extension context.
   * @returns {RTETableRowBlock[]} The selected table row blocks.
   */
  getAllSelectedRows(extensionContext) {
    const selectedRows = new Set();
    if (extensionContext && extensionContext.tableSelection) {
      const { tableSelection } = extensionContext;
      const selectedCells = tableSelection.getSelectedCells();
      selectedCells.forEach((cell) => {
        const rowBlock = getParentBlockByType(cell, RTETableRowBlock);
        if (rowBlock) {
          selectedRows.add(rowBlock);
        }
      });
    }
    return Array.from(selectedRows);
  }
  /**
   * Applies the given action to the table containing the reference block.
   *
   * @param {RTEBlock} refBlock - The reference block within the table.
   * @param {ModifyContentAction} action - The action to apply to the table.
   */
  applyActionToTable(refBlock, action) {
    const table = getParentBlockByType(refBlock, RTETableBlock);
    if (table) {
      table.apply(action);
    }
  }
}
