import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridCellEditing } from './useGridCellEditing';
import { GridCellModes, GridEditModes } from '../../../models/gridEditRowModel';
import { useGridRowEditing } from './useGridRowEditing';
import { gridEditRowsStateSelector } from './gridEditingSelectors';
import { isAutoGeneratedRow } from '../rows/gridRowsUtils';
export const editingStateInitializer = state => _extends({}, state, {
  editRows: {}
});
export const useGridEditing = (apiRef, props) => {
  useGridCellEditing(apiRef, props);
  useGridRowEditing(apiRef, props);
  const debounceMap = React.useRef({});
  const {
    isCellEditable: isCellEditableProp
  } = props;
  const isCellEditable = React.useCallback(params => {
    if (isAutoGeneratedRow(params.rowNode)) {
      return false;
    }
    if (!params.colDef.editable) {
      return false;
    }
    if (!params.colDef.renderEditCell) {
      return false;
    }
    if (isCellEditableProp) {
      return isCellEditableProp(params);
    }
    return true;
  }, [isCellEditableProp]);
  const maybeDebounce = (id, field, debounceMs, callback) => {
    if (!debounceMs) {
      callback();
      return;
    }
    if (!debounceMap.current[id]) {
      debounceMap.current[id] = {};
    }
    if (debounceMap.current[id][field]) {
      const [timeout] = debounceMap.current[id][field];
      clearTimeout(timeout);
    }

    // To run the callback immediately without waiting the timeout
    const runImmediately = () => {
      const [timeout] = debounceMap.current[id][field];
      clearTimeout(timeout);
      callback();
      delete debounceMap.current[id][field];
    };
    const timeout = setTimeout(() => {
      callback();
      delete debounceMap.current[id][field];
    }, debounceMs);
    debounceMap.current[id][field] = [timeout, runImmediately];
  };
  React.useEffect(() => {
    const debounces = debounceMap.current;
    return () => {
      Object.entries(debounces).forEach(([id, fields]) => {
        Object.keys(fields).forEach(field => {
          const [timeout] = debounces[id][field];
          clearTimeout(timeout);
          delete debounces[id][field];
        });
      });
    };
  }, []);
  const runPendingEditCellValueMutation = React.useCallback((id, field) => {
    if (!debounceMap.current[id]) {
      return;
    }
    if (!field) {
      Object.keys(debounceMap.current[id]).forEach(debouncedField => {
        const [, runCallback] = debounceMap.current[id][debouncedField];
        runCallback();
      });
    } else if (debounceMap.current[id][field]) {
      const [, runCallback] = debounceMap.current[id][field];
      runCallback();
    }
  }, []);
  const setEditCellValue = React.useCallback(params => {
    const {
      id,
      field,
      debounceMs
    } = params;
    return new Promise(resolve => {
      maybeDebounce(id, field, debounceMs, async () => {
        const setEditCellValueToCall = props.editMode === GridEditModes.Row ? apiRef.current.setRowEditingEditCellValue : apiRef.current.setCellEditingEditCellValue;

        // Check if the cell is in edit mode
        // By the time this callback runs the user may have cancelled the editing
        if (apiRef.current.getCellMode(id, field) === GridCellModes.Edit) {
          const result = await setEditCellValueToCall(params);
          resolve(result);
        }
      });
    });
  }, [apiRef, props.editMode]);
  const getRowWithUpdatedValues = React.useCallback((id, field) => {
    return props.editMode === GridEditModes.Cell ? apiRef.current.getRowWithUpdatedValuesFromCellEditing(id, field) : apiRef.current.getRowWithUpdatedValuesFromRowEditing(id);
  }, [apiRef, props.editMode]);
  const getEditCellMeta = React.useCallback((id, field) => {
    const editingState = gridEditRowsStateSelector(apiRef.current.state);
    return editingState[id][field];
  }, [apiRef]);
  const editingSharedApi = {
    isCellEditable,
    setEditCellValue,
    getRowWithUpdatedValues,
    unstable_getEditCellMeta: getEditCellMeta
  };
  const editingSharedPrivateApi = {
    runPendingEditCellValueMutation
  };
  useGridApiMethod(apiRef, editingSharedApi, 'public');
  useGridApiMethod(apiRef, editingSharedPrivateApi, 'private');
};