import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { GridSignature, useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridRowsLookupSelector } from '../rows/gridRowsSelector';
import { gridRowSelectionStateSelector, selectedGridRowsSelector, selectedIdsLookupSelector } from './gridRowSelectionSelector';
import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../pagination';
import { gridFocusCellSelector } from '../focus/gridFocusStateSelector';
import { gridExpandedSortedRowIdsSelector } from '../filter/gridFilterSelector';
import { GRID_CHECKBOX_SELECTION_COL_DEF, GRID_ACTIONS_COLUMN_TYPE } from '../../../colDef';
import { GridCellModes } from '../../../models/gridEditRowModel';
import { isKeyboardEvent, isNavigationKey } from '../../../utils/keyboardUtils';
import { useGridVisibleRows } from '../../utils/useGridVisibleRows';
import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from '../../../constants/gridDetailPanelToggleField';
import { gridClasses } from '../../../constants/gridClasses';
var getSelectionModelPropValue = function getSelectionModelPropValue(selectionModelProp, prevSelectionModel) {
  if (selectionModelProp == null) {
    return selectionModelProp;
  }
  if (Array.isArray(selectionModelProp)) {
    return selectionModelProp;
  }
  if (prevSelectionModel && prevSelectionModel[0] === selectionModelProp) {
    return prevSelectionModel;
  }
  return [selectionModelProp];
};
export var rowSelectionStateInitializer = function rowSelectionStateInitializer(state, props) {
  var _getSelectionModelPro;
  return _extends({}, state, {
    rowSelection: props.rowSelection ? (_getSelectionModelPro = getSelectionModelPropValue(props.rowSelectionModel)) != null ? _getSelectionModelPro : [] : []
  });
};

/**
 * @requires useGridRows (state, method) - can be after
 * @requires useGridParamsApi (method) - can be after
 * @requires useGridFocus (state) - can be after
 * @requires useGridKeyboardNavigation (`cellKeyDown` event must first be consumed by it)
 */
export var useGridRowSelection = function useGridRowSelection(apiRef, props) {
  var logger = useGridLogger(apiRef, 'useGridSelection');
  var runIfRowSelectionIsEnabled = function runIfRowSelectionIsEnabled(callback) {
    return function () {
      if (props.rowSelection) {
        callback.apply(void 0, arguments);
      }
    };
  };
  var propRowSelectionModel = React.useMemo(function () {
    return getSelectionModelPropValue(props.rowSelectionModel, gridRowSelectionStateSelector(apiRef.current.state));
  }, [apiRef, props.rowSelectionModel]);
  var lastRowToggled = React.useRef(null);
  apiRef.current.registerControlState({
    stateId: 'rowSelection',
    propModel: propRowSelectionModel,
    propOnChange: props.onRowSelectionModelChange,
    stateSelector: gridRowSelectionStateSelector,
    changeEvent: 'rowSelectionChange'
  });
  var checkboxSelection = props.checkboxSelection,
    disableMultipleRowSelection = props.disableMultipleRowSelection,
    disableRowSelectionOnClick = props.disableRowSelectionOnClick,
    propIsRowSelectable = props.isRowSelectable;
  var canHaveMultipleSelection = !disableMultipleRowSelection || checkboxSelection;
  var visibleRows = useGridVisibleRows(apiRef, props);
  var expandMouseRowRangeSelection = React.useCallback(function (id) {
    var _lastRowToggled$curre;
    var endId = id;
    var startId = (_lastRowToggled$curre = lastRowToggled.current) != null ? _lastRowToggled$curre : id;
    var isSelected = apiRef.current.isRowSelected(id);
    if (isSelected) {
      var visibleRowIds = gridExpandedSortedRowIdsSelector(apiRef);
      var startIndex = visibleRowIds.findIndex(function (rowId) {
        return rowId === startId;
      });
      var endIndex = visibleRowIds.findIndex(function (rowId) {
        return rowId === endId;
      });
      if (startIndex === endIndex) {
        return;
      }
      if (startIndex > endIndex) {
        endId = visibleRowIds[endIndex + 1];
      } else {
        endId = visibleRowIds[endIndex - 1];
      }
    }
    lastRowToggled.current = id;
    apiRef.current.selectRowRange({
      startId: startId,
      endId: endId
    }, !isSelected);
  }, [apiRef]);

  /**
   * API METHODS
   */
  var setRowSelectionModel = React.useCallback(function (model) {
    if (props.signature === GridSignature.DataGrid && !props.checkboxSelection && Array.isArray(model) && model.length > 1) {
      throw new Error(['MUI: `rowSelectionModel` can only contain 1 item in DataGrid.', 'You need to upgrade to DataGridPro or DataGridPremium component to unlock multiple selection.'].join('\n'));
    }
    var currentModel = gridRowSelectionStateSelector(apiRef.current.state);
    if (currentModel !== model) {
      logger.debug("Setting selection model");
      apiRef.current.setState(function (state) {
        return _extends({}, state, {
          rowSelection: props.rowSelection ? model : []
        });
      });
      apiRef.current.forceUpdate();
    }
  }, [apiRef, logger, props.rowSelection, props.signature, props.checkboxSelection]);
  var isRowSelected = React.useCallback(function (id) {
    return gridRowSelectionStateSelector(apiRef.current.state).includes(id);
  }, [apiRef]);
  var isRowSelectable = React.useCallback(function (id) {
    if (propIsRowSelectable && !propIsRowSelectable(apiRef.current.getRowParams(id))) {
      return false;
    }
    var rowNode = apiRef.current.getRowNode(id);
    if ((rowNode == null ? void 0 : rowNode.type) === 'footer' || (rowNode == null ? void 0 : rowNode.type) === 'pinnedRow') {
      return false;
    }
    return true;
  }, [apiRef, propIsRowSelectable]);
  var getSelectedRows = React.useCallback(function () {
    return selectedGridRowsSelector(apiRef);
  }, [apiRef]);
  var selectRow = React.useCallback(function (id) {
    var isSelected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    var resetSelection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    if (!apiRef.current.isRowSelectable(id)) {
      return;
    }
    lastRowToggled.current = id;
    if (resetSelection) {
      logger.debug("Setting selection for row ".concat(id));
      apiRef.current.setRowSelectionModel(isSelected ? [id] : []);
    } else {
      logger.debug("Toggling selection for row ".concat(id));
      var selection = gridRowSelectionStateSelector(apiRef.current.state);
      var newSelection = selection.filter(function (el) {
        return el !== id;
      });
      if (isSelected) {
        newSelection.push(id);
      }
      var isSelectionValid = newSelection.length < 2 || canHaveMultipleSelection;
      if (isSelectionValid) {
        apiRef.current.setRowSelectionModel(newSelection);
      }
    }
  }, [apiRef, logger, canHaveMultipleSelection]);
  var selectRows = React.useCallback(function (ids) {
    var isSelected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    var resetSelection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    logger.debug("Setting selection for several rows");
    var selectableIds = ids.filter(function (id) {
      return apiRef.current.isRowSelectable(id);
    });
    var newSelection;
    if (resetSelection) {
      newSelection = isSelected ? selectableIds : [];
    } else {
      // We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
      var selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
      selectableIds.forEach(function (id) {
        if (isSelected) {
          selectionLookup[id] = id;
        } else {
          delete selectionLookup[id];
        }
      });
      newSelection = Object.values(selectionLookup);
    }
    var isSelectionValid = newSelection.length < 2 || canHaveMultipleSelection;
    if (isSelectionValid) {
      apiRef.current.setRowSelectionModel(newSelection);
    }
  }, [apiRef, logger, canHaveMultipleSelection]);
  var selectRowRange = React.useCallback(function (_ref) {
    var startId = _ref.startId,
      endId = _ref.endId;
    var isSelected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    var resetSelection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    if (!apiRef.current.getRow(startId) || !apiRef.current.getRow(endId)) {
      return;
    }
    logger.debug("Expanding selection from row ".concat(startId, " to row ").concat(endId));

    // Using rows from all pages allow to select a range across several pages
    var allPagesRowIds = gridExpandedSortedRowIdsSelector(apiRef);
    var startIndex = allPagesRowIds.indexOf(startId);
    var endIndex = allPagesRowIds.indexOf(endId);
    var _ref2 = startIndex > endIndex ? [endIndex, startIndex] : [startIndex, endIndex],
      _ref3 = _slicedToArray(_ref2, 2),
      start = _ref3[0],
      end = _ref3[1];
    var rowsBetweenStartAndEnd = allPagesRowIds.slice(start, end + 1);
    apiRef.current.selectRows(rowsBetweenStartAndEnd, isSelected, resetSelection);
  }, [apiRef, logger]);
  var selectionPublicApi = {
    selectRow: selectRow,
    setRowSelectionModel: setRowSelectionModel,
    getSelectedRows: getSelectedRows,
    isRowSelected: isRowSelected,
    isRowSelectable: isRowSelectable
  };
  var selectionPrivateApi = {
    selectRows: selectRows,
    selectRowRange: selectRowRange
  };
  useGridApiMethod(apiRef, selectionPublicApi, 'public');
  useGridApiMethod(apiRef, selectionPrivateApi, props.signature === GridSignature.DataGrid ? 'private' : 'public');

  /**
   * EVENTS
   */
  var removeOutdatedSelection = React.useCallback(function () {
    if (props.keepNonExistentRowsSelected) {
      return;
    }
    var currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
    var rowsLookup = gridRowsLookupSelector(apiRef);

    // We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
    var selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
    var hasChanged = false;
    currentSelection.forEach(function (id) {
      if (!rowsLookup[id]) {
        delete selectionLookup[id];
        hasChanged = true;
      }
    });
    if (hasChanged) {
      apiRef.current.setRowSelectionModel(Object.values(selectionLookup));
    }
  }, [apiRef, props.keepNonExistentRowsSelected]);
  var handleSingleRowSelection = React.useCallback(function (id, event) {
    var hasCtrlKey = event.metaKey || event.ctrlKey;

    // multiple selection is only allowed if:
    // - it is a checkboxSelection
    // - it is a keyboard selection
    // - Ctrl is pressed

    var isMultipleSelectionDisabled = !checkboxSelection && !hasCtrlKey && !isKeyboardEvent(event);
    var resetSelection = !canHaveMultipleSelection || isMultipleSelectionDisabled;
    var isSelected = apiRef.current.isRowSelected(id);
    if (resetSelection) {
      apiRef.current.selectRow(id, !isMultipleSelectionDisabled ? !isSelected : true, true);
    } else {
      apiRef.current.selectRow(id, !isSelected, false);
    }
  }, [apiRef, canHaveMultipleSelection, checkboxSelection]);
  var handleRowClick = React.useCallback(function (params, event) {
    var _closest;
    if (disableRowSelectionOnClick) {
      return;
    }
    var field = (_closest = event.target.closest(".".concat(gridClasses.cell))) == null ? void 0 : _closest.getAttribute('data-field');
    if (field === GRID_CHECKBOX_SELECTION_COL_DEF.field) {
      // click on checkbox should not trigger row selection
      return;
    }
    if (field === GRID_DETAIL_PANEL_TOGGLE_FIELD) {
      // click to open the detail panel should not select the row
      return;
    }
    if (field) {
      var column = apiRef.current.getColumn(field);
      if (column.type === GRID_ACTIONS_COLUMN_TYPE) {
        return;
      }
    }
    var rowNode = apiRef.current.getRowNode(params.id);
    if (rowNode.type === 'pinnedRow') {
      return;
    }
    if (event.shiftKey && (canHaveMultipleSelection || checkboxSelection)) {
      expandMouseRowRangeSelection(params.id);
    } else {
      handleSingleRowSelection(params.id, event);
    }
  }, [disableRowSelectionOnClick, canHaveMultipleSelection, checkboxSelection, apiRef, expandMouseRowRangeSelection, handleSingleRowSelection]);
  var preventSelectionOnShift = React.useCallback(function (params, event) {
    if (canHaveMultipleSelection && event.shiftKey) {
      var _window$getSelection;
      (_window$getSelection = window.getSelection()) == null ? void 0 : _window$getSelection.removeAllRanges();
    }
  }, [canHaveMultipleSelection]);
  var handleRowSelectionCheckboxChange = React.useCallback(function (params, event) {
    if (event.nativeEvent.shiftKey) {
      expandMouseRowRangeSelection(params.id);
    } else {
      apiRef.current.selectRow(params.id, params.value);
    }
  }, [apiRef, expandMouseRowRangeSelection]);
  var handleHeaderSelectionCheckboxChange = React.useCallback(function (params) {
    var shouldLimitSelectionToCurrentPage = props.checkboxSelectionVisibleOnly && props.pagination;
    var rowsToBeSelected = shouldLimitSelectionToCurrentPage ? gridPaginatedVisibleSortedGridRowIdsSelector(apiRef) : gridExpandedSortedRowIdsSelector(apiRef);
    apiRef.current.selectRows(rowsToBeSelected, params.value);
  }, [apiRef, props.checkboxSelectionVisibleOnly, props.pagination]);
  var handleCellKeyDown = React.useCallback(function (params, event) {
    // Get the most recent cell mode because it may have been changed by another listener
    if (apiRef.current.getCellMode(params.id, params.field) === GridCellModes.Edit) {
      return;
    }

    // Ignore portal
    // Do not apply shortcuts if the focus is not on the cell root component
    if (!event.currentTarget.contains(event.target)) {
      return;
    }
    if (isNavigationKey(event.key) && event.shiftKey) {
      // The cell that has focus after the keyboard navigation
      var focusCell = gridFocusCellSelector(apiRef);
      if (focusCell && focusCell.id !== params.id) {
        event.preventDefault();
        var isNextRowSelected = apiRef.current.isRowSelected(focusCell.id);
        if (!canHaveMultipleSelection) {
          apiRef.current.selectRow(focusCell.id, !isNextRowSelected, true);
          return;
        }
        var newRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(focusCell.id);
        var previousRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(params.id);
        var start;
        var end;
        if (newRowIndex > previousRowIndex) {
          if (isNextRowSelected) {
            // We are navigating to the bottom of the page and adding selected rows
            start = previousRowIndex;
            end = newRowIndex - 1;
          } else {
            // We are navigating to the bottom of the page and removing selected rows
            start = previousRowIndex;
            end = newRowIndex;
          }
        } else {
          // eslint-disable-next-line no-lonely-if
          if (isNextRowSelected) {
            // We are navigating to the top of the page and removing selected rows
            start = newRowIndex + 1;
            end = previousRowIndex;
          } else {
            // We are navigating to the top of the page and adding selected rows
            start = newRowIndex;
            end = previousRowIndex;
          }
        }
        var rowsBetweenStartAndEnd = visibleRows.rows.slice(start, end + 1).map(function (row) {
          return row.id;
        });
        apiRef.current.selectRows(rowsBetweenStartAndEnd, !isNextRowSelected);
        return;
      }
    }
    if (event.key === ' ' && event.shiftKey) {
      event.preventDefault();
      handleSingleRowSelection(params.id, event);
      return;
    }
    if (event.key === 'a' && (event.ctrlKey || event.metaKey)) {
      event.preventDefault();
      selectRows(apiRef.current.getAllRowIds(), true);
    }
  }, [apiRef, handleSingleRowSelection, selectRows, visibleRows.rows, canHaveMultipleSelection]);
  useGridApiEventHandler(apiRef, 'sortedRowsSet', runIfRowSelectionIsEnabled(removeOutdatedSelection));
  useGridApiEventHandler(apiRef, 'rowClick', runIfRowSelectionIsEnabled(handleRowClick));
  useGridApiEventHandler(apiRef, 'rowSelectionCheckboxChange', runIfRowSelectionIsEnabled(handleRowSelectionCheckboxChange));
  useGridApiEventHandler(apiRef, 'headerSelectionCheckboxChange', handleHeaderSelectionCheckboxChange);
  useGridApiEventHandler(apiRef, 'cellMouseDown', runIfRowSelectionIsEnabled(preventSelectionOnShift));
  useGridApiEventHandler(apiRef, 'cellKeyDown', runIfRowSelectionIsEnabled(handleCellKeyDown));

  /**
   * EFFECTS
   */
  React.useEffect(function () {
    if (propRowSelectionModel !== undefined) {
      apiRef.current.setRowSelectionModel(propRowSelectionModel);
    }
  }, [apiRef, propRowSelectionModel, props.rowSelection]);
  React.useEffect(function () {
    if (!props.rowSelection) {
      apiRef.current.setRowSelectionModel([]);
    }
  }, [apiRef, props.rowSelection]);
  var isStateControlled = propRowSelectionModel != null;
  React.useEffect(function () {
    if (isStateControlled || !props.rowSelection) {
      return;
    }

    // props.isRowSelectable changed
    var currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
    if (isRowSelectable) {
      var newSelection = currentSelection.filter(function (id) {
        return isRowSelectable(id);
      });
      if (newSelection.length < currentSelection.length) {
        apiRef.current.setRowSelectionModel(newSelection);
      }
    }
  }, [apiRef, isRowSelectable, isStateControlled, props.rowSelection]);
  React.useEffect(function () {
    if (!props.rowSelection || isStateControlled) {
      return;
    }
    var currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
    if (!canHaveMultipleSelection && currentSelection.length > 1) {
      // See https://github.com/mui/mui-x/issues/8455
      apiRef.current.setRowSelectionModel([]);
    }
  }, [apiRef, canHaveMultipleSelection, checkboxSelection, isStateControlled, props.rowSelection]);
};