import type { CellType, ValidationRule } from '../../state/EditableTable/types';

export interface CellViewProps<V> {
  value: V;
  typeConfig?: unknown;
  validation?: ValidationRule[];
  /** Stable id of the column. Cell components can use it to derive unique DOM ids. */
  columnId: string;
  /** Stable key of the row. Cell components can use it to derive unique DOM ids. */
  rowKey: string;
}

export interface Focusable {
  focus: () => void;
}

export type EditTrigger =
  | { kind: 'type'; key: string }
  | 'enter'
  | 'click'
  | 'doubleClick';

export interface CellEditProps<V> {
  value: V;
  onChange: (value: V) => void;
  onCommit: () => void;
  onCancel: () => void;
  /**
   * Signals that the cell is starting an editing operation that will move focus
   * outside the table (e.g., opening a media manager or a modal). Calling this
   * sets isEditing=true so useClearFocusOnBlur does not clear cell focus while
   * the external UI is open. Pair with onCommit (after the operation succeeds)
   * or onCancel (if the user dismisses without a value change).
   */
  onStartEdit?: () => void;
  /** Ref to the focusable input element. Used for type-to-edit (focusing the input when user starts typing on a focused cell). */
  inputRef?: React.MutableRefObject<Focusable | undefined | null>;
  typeConfig?: unknown;
  /** How edit mode was triggered. Edit components can use this to customize behavior (e.g., date picker auto-opens on 'type'). */
  editTrigger?: EditTrigger | null;
  /** Column-level validation rules. Edit components can derive UI behavior (e.g., maxLength blocking/tooltip) from these. */
  validation?: ValidationRule[];
  /** Stable id of the column. Cell components can use it to derive unique DOM ids. */
  columnId: string;
  /** Stable key of the row. Cell components can use it to derive unique DOM ids. */
  rowKey: string;
}

/**
 * Props passed to a cell type's optional `EditWrapper`. EditableCell mounts the
 * wrapper around the cell content and keeps it mounted across the edit↔view
 * transition, so the wrapper can own behavior that must outlive the inline
 * editor (e.g. a reference cell navigating to an overlay and refreshing on
 * return). The wrapper hands anything the editor needs down via its own context.
 */
export interface CellEditWrapperProps {
  /** The column's typeConfig (same value handed to the cell's Edit/View). */
  typeConfig?: unknown;
  /** Stable id of the column. */
  columnId: string;
  /** Stable key of the row. */
  rowKey: string;
  /** The cell content (the Edit or View component) to render inside the wrapper. */
  children: React.ReactNode;
}

/**
 * Type-safe definition for a cell type. Non-value-typed props are derived
 * directly from CellType via Pick so new additions propagate automatically.
 */
export type CellTypeDefinition<V> = Pick<
  CellType,
  | 'editable'
  | 'clearable'
  | 'showEditWhenFocused'
  | 'nativeTypeToEdit'
  | 'EditWrapper'
  | 'growable'
> & {
  type: CellType['type'];
  ViewComponent: React.ComponentType<CellViewProps<V>>;
  EditComponent?: React.ComponentType<CellEditProps<V>>;
  serialize?: (value: V) => string;
  deserialize?: (raw: string) => V;
  validate?: (value: V, rules?: ValidationRule[]) => string | null;
  toggleValue?: (value: V) => V;
  editOnClick?: (value: V) => boolean;
  onTypeToEdit?: (params: {
    key: string;
    validation?: ValidationRule[];
  }) => { value?: V } | undefined;
  normalizeOnCommit?: (value: V) => V;
};

/** Type-safe factory for defining cell types. V ensures all value-typed fields are consistent. */
export function defineCellType<V>(cellType: CellTypeDefinition<V>): CellType {
  return cellType as CellType;
}
