import { CellFocusedEvent, IRowNode } from 'ag-grid-community';
import { CustomCellEditorProps } from 'ag-grid-react';
import clsx from 'clsx';
import { useCallback, useEffect, useRef } from 'react';

import { useGridContext } from '../../contexts/GridContext';
import { fnOrVar } from '../../utils/util';
import { clickInputWhenContainingCellClicked } from '../clickInputWhenContainingCellClicked';
import { CellEditorCommon, GridCell } from '../GridCell';
import { GenericCellColDef } from '../gridRender';
import { ColDefT, GridBaseRow } from '../types';
import { useSharedInterval } from '../../utils/useSharedInterval';

const BooleanCellRenderer = (props: CustomCellEditorProps) => {
  const { onValueChange, value, api, node, column, colDef, data, eGridCell } = props;
  const inputRef = useRef<HTMLInputElement>(null);
  const { updatingCells, redrawRows, resetFocusedCellAfterCellEditing, prePopupOps } = useGridContext();

  useEffect(() => {
    const checkFocus = (event: CellFocusedEvent) => {
      if (event.rowIndex === node.rowIndex && event.column === column) {
        inputRef.current?.focus();
      }
    };
    api.addEventListener('cellFocused', checkFocus);
    return () => void api.removeEventListener('cellFocused', checkFocus);
  }, [api, column, node.rowIndex]);

  const isDisabled = !fnOrVar(colDef?.editable, props);

  const toggleCheckbox = useCallback(() => {
    if (!onValueChange) {
      return;
    }

    const params = props?.colDef?.cellEditorParams as GridEditBooleanEditorProps<any> | undefined;
    if (!params) {
      return;
    }

    const nodes: IRowNode[] = [];
    api.forEachNode((n) => {
      if (n.data.id === data.id) {
        nodes.push(n);
      }
    });

    prePopupOps();

    const checked = !value;
    onValueChange(checked);
    const field = props.colDef?.field ?? props.colDef?.colId ?? '';
    const selectedRows = nodes.map((node) => node.data);
    void updatingCells({ selectedRows, field }, async () => {
      redrawRows(nodes);
      return params.onClick({ selectedRows, selectedRowIds: selectedRows.map((r) => r.id), checked });
    }).finally(() => {
      resetFocusedCellAfterCellEditing();
    });
  }, [
    api,
    data.id,
    onValueChange,
    prePopupOps,
    props.colDef?.cellEditorParams,
    props.colDef?.colId,
    props.colDef?.field,
    redrawRows,
    resetFocusedCellAfterCellEditing,
    updatingCells,
    value,
  ]);

  useSharedInterval(() => {
    if (isDisabled) {
      return;
    }
    const cell: HTMLElement | null = eGridCell?.querySelector(
      '.ag-cell-focus .grid-edit-boolean input.ag-checkbox-input:not(:disabled)',
    );
    if (cell) {
      // When you redraw a cell the activeElement moves to the cell div away from input
      // Refocus here if the cell div is active back to the input
      const activeElement = cell.ownerDocument.activeElement;
      if (activeElement && activeElement.getAttribute('role') === 'gridcell' && activeElement !== cell) {
        cell.focus();
      }
    }
  });

  return (
    <div
      className={clsx('grid-edit-boolean ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper', {
        'ag-checked': props.value,
        'ag-disabled': isDisabled,
      })}
    >
      <input
        type="checkbox"
        className="ag-input-field-input ag-checkbox-input"
        disabled={isDisabled}
        ref={inputRef}
        checked={value}
        onChange={() => {}}
        onClick={(e) => {
          e.stopPropagation();
          toggleCheckbox();
        }}
      />
    </div>
  );
};

export interface GridEditBooleanEditorProps<TData extends GridBaseRow> extends CellEditorCommon {
  onClick: (props: {
    selectedRows: TData[];
    selectedRowIds: TData['id'][];
    checked: boolean;
  }) => Promise<boolean> | boolean;
}

export const GridEditBoolean = <TData extends GridBaseRow>(
  colDef: GenericCellColDef<TData, boolean>,
  editorProps: GridEditBooleanEditorProps<TData>,
): ColDefT<TData> => {
  return GridCell<TData>({
    minWidth: 64,
    maxWidth: 64,
    cellRenderer: BooleanCellRenderer as any,
    cellEditor: BooleanCellRenderer,
    cellEditorParams: editorProps,
    onCellClicked: clickInputWhenContainingCellClicked,
    singleClickEdit: true,
    resizable: false,
    editable: true,
    cellClass: 'GridCellAlignCenter GridCellEditableWithNoPopup',
    headerClass: 'GridHeaderAlignCenter',
    ...colDef,
  });
};
