import React, { useCallback, useRef } from 'react';
import { observer } from 'mobx-react-lite';
import type { FiltersMap } from '@wix/bex-core';
import { Tooltip } from '@wix/design-system';
import { StatusWarningFilledSmall } from '@wix/wix-ui-icons-common';
import { st, classes } from './EditableCell.st.css.js';
import type { EditableTableState } from '../../state/EditableTable';
import { useCellFocusAndEditing } from './useCellFocusAndEditing';
import { Focusable } from './types';

export interface EditableCellProps<T, F extends FiltersMap> {
  state: EditableTableState<T, F>;
  rowKey: string;
  columnId: string;
}

function _EditableCell<T, F extends FiltersMap>({
  state,
  rowKey,
  columnId,
}: EditableCellProps<T, F>) {
  const { cellInteraction, clipboard } = state;
  const cellRef = useRef<HTMLDivElement | null>(null);
  const editInputRef = useRef<Focusable>();

  const column = state._columnMap.get(columnId);
  const keyedItem = state._getKeyedItem(rowKey);
  const resolved = column ? state.resolveCellTypeByColumnId(column.id) : null;
  const value = state.getCellDisplayValue(rowKey, columnId);
  const cellValidation = state.getCellValidation(rowKey, columnId);

  const isFocused = cellInteraction.isCellFocused(rowKey, columnId);
  const isEditing = cellInteraction.isCellEditing(rowKey, columnId);
  const isSelected = cellInteraction.isCellSelected(rowKey, columnId);
  const selectionBorder = cellInteraction.getSelectionBorder(rowKey, columnId);
  const copiedBorder = clipboard.getCopiedBorder(rowKey, columnId);
  const isEditable = state.isCellEditable(rowKey, columnId);

  const handleClick = useCallback(
    (e: React.MouseEvent) => {
      e.stopPropagation();
      state.handleCellClick(rowKey, columnId, e.shiftKey);
    },
    [state, rowKey, columnId],
  );

  const handleDoubleClick = useCallback(
    (e: React.MouseEvent) => {
      e.stopPropagation();
      e.preventDefault();
      state.handleCellDoubleClick(rowKey, columnId);
    },
    [state, rowKey, columnId],
  );

  // Auto-start edit if onChange fires before explicit startEdit (e.g., onOptionMarked
  // in select). startEdit resets editingValue, so it must come before setEditingValue.
  // For production cell types, consider the per-callback withEnterEdit pattern from cms-web.
  const handleChange = useCallback(
    (newValue: any) => {
      if (!cellInteraction.isEditing) {
        cellInteraction.startEdit(rowKey, columnId, 'enter');
      }
      cellInteraction.setEditingValue(newValue);
    },
    [cellInteraction, rowKey, columnId],
  );

  const handleCommit = useCallback(
    () => cellInteraction.commitEdit(),
    [cellInteraction],
  );

  const handleCancel = useCallback(
    () => cellInteraction.cancelEdit(),
    [cellInteraction],
  );

  const handleStartEdit = useCallback(() => {
    if (!cellInteraction.isEditing) {
      cellInteraction.startEdit(rowKey, columnId);
    }
  }, [cellInteraction, rowKey, columnId]);

  const cellKeyboard = useCellFocusAndEditing({
    state,
    rowKey,
    columnId,
    cellRef,
    editInputRef,
  });

  if (!column || !keyedItem || !resolved) {
    return null;
  }

  const { ViewComponent, EditComponent } = resolved;
  const showValidationIndicator = !cellValidation.valid && !isEditing;

  const showEdit =
    (isEditing ||
      (isFocused &&
        isEditable &&
        !state.updateItemDisabled &&
        (!!resolved.toggleValue || !!resolved.showEditWhenFocused))) &&
    EditComponent;

  const cellContent = showEdit ? (
    <EditComponent
      value={
        cellInteraction.editingValue !== undefined
          ? cellInteraction.editingValue
          : value
      }
      onChange={handleChange}
      onCommit={handleCommit}
      onCancel={handleCancel}
      onStartEdit={handleStartEdit}
      inputRef={editInputRef}
      typeConfig={column.typeConfig}
      editTrigger={cellInteraction.editTrigger}
      validation={column.validation}
      columnId={columnId}
      rowKey={rowKey}
    />
  ) : (
    <ViewComponent
      value={value}
      typeConfig={column.typeConfig}
      validation={column.validation}
      columnId={columnId}
      rowKey={rowKey}
    />
  );

  // The cell type's EditWrapper (if any) stays mounted across the edit↔view
  // swap, so it can own behavior that must outlive the inline editor.
  const EditWrapper = resolved.EditWrapper;

  return (
    <div
      ref={cellRef}
      className={st(classes.root, {
        editing: isEditing,
        focused: isFocused,
        selected: isSelected,
        invalid: !cellValidation.valid,
        typeToEdit: !!resolved.onTypeToEdit,
        showEditWhenFocused: !!resolved.showEditWhenFocused,
        growable: !!resolved.growable,
        selectionTop: selectionBorder.top,
        selectionBottom: selectionBorder.bottom,
        selectionLeft: selectionBorder.left,
        selectionRight: selectionBorder.right,
        copiedTop: copiedBorder.top,
        copiedBottom: copiedBorder.bottom,
        copiedLeft: copiedBorder.left,
        copiedRight: copiedBorder.right,
      })}
      onClick={handleClick}
      onDoubleClick={handleDoubleClick}
      onKeyDown={cellKeyboard.onKeyDown}
      role="gridcell"
      aria-selected={isSelected}
      aria-readonly={!isEditable}
      aria-invalid={!cellValidation.valid || undefined}
      tabIndex={isFocused ? 0 : -1}
      data-row-key={rowKey}
      data-column-id={columnId}
      data-editing={isEditing || undefined}
    >
      {showValidationIndicator && (
        <Tooltip content={cellValidation.errors?.join(', ')} appendTo="window">
          <span data-hook="validation-indicator">
            <StatusWarningFilledSmall className={classes.validationIndicator} />
          </span>
        </Tooltip>
      )}
      {EditWrapper ? (
        <EditWrapper
          typeConfig={column.typeConfig}
          columnId={columnId}
          rowKey={rowKey}
        >
          {cellContent}
        </EditWrapper>
      ) : (
        cellContent
      )}
    </div>
  );
}

export const EditableCell = observer(_EditableCell);
