import type { ComponentType } from 'react';
import type { RichTextStyleVariables } from '@wix/patterns-fields';
import type { TableColumn } from '../../model/tableColumn';
import type { ReferenceTypeConfig } from '../../components/EditableTable/cellTypes/reference/types';
import type { AddressCellTypeConfig } from '../../components/EditableTable/cellTypes/address';
import type {
  CellViewProps,
  CellEditProps,
  CellEditWrapperProps,
} from '../../components/EditableTable/types';

export interface RichTextCellTypeConfig {
  textStyle?: RichTextStyleVariables;
}

export interface Cell {
  rowKey: string;
  columnId: string;
}

export interface CellBorder {
  top: boolean;
  bottom: boolean;
  left: boolean;
  right: boolean;
}

export type ValidationRule =
  | { type: 'required'; message: string }
  | { type: 'min' | 'max'; value: number; message: string }
  | { type: 'minLength' | 'maxLength'; value: number; message: string }
  | { type: 'pattern'; value: RegExp; message: string }
  | { type: 'custom'; validate: (value: unknown) => boolean; message: string }
  | {
      type: 'specificValues';
      values: { id: string; value: string }[];
      message: string;
    };

export interface ValidationResult {
  valid: boolean;
  errors?: string[];
}

export interface CellType {
  type: keyof CellTypeConfigMap;

  /** Render cell in view mode (read-only display) */
  ViewComponent: ComponentType<CellViewProps<any>>;

  /** Render cell in edit mode (inline editor). Optional — omit for view-only cells. */
  EditComponent?: ComponentType<CellEditProps<any>>;

  /** Serialize value to clipboard string */
  serialize?: (value: any) => string;

  /** Deserialize clipboard string to value */
  deserialize?: (raw: string) => any;

  /** Validate a cell value. Return null if valid, or error message string. */
  validate?: (value: any, rules?: ValidationRule[]) => string | null;

  /** Can this cell type be edited? (default: true) */
  editable?: boolean;

  /** Can this cell type be cleared with Delete key? (default: true) */
  clearable?: boolean;

  /** Toggle value on single click (e.g., boolean checkbox). Skips focus→edit flow. */
  toggleValue?: (value: any) => any;

  /** Enter edit mode on single click (e.g., image cell with no value). */
  editOnClick?: (value: any) => boolean;

  /** Show the EditComponent when the cell is focused, without entering edit mode.
   *  Keyboard navigation remains active. Similar to toggleValue but without toggling. */
  showEditWhenFocused?: boolean;

  /** Optional wrapper component the cell type defines to own behavior that must
   *  outlive the inline editor. EditableCell mounts it around the cell content
   *  and keeps it mounted across the edit↔view transition (e.g. a reference cell
   *  navigating to an overlay and refreshing the table on return). The wrapper
   *  hands anything the editor needs down via its own context. */
  EditWrapper?: ComponentType<CellEditWrapperProps>;

  /** Cell content can grow past row height when focused (e.g. multi-document
   *  wraps chips into multiple rows). The framework releases the bottom anchor
   *  and lays an opaque base under the selection tint so the overflow doesn't
   *  bleed adjacent rows through.
   *  Only applies to showEditWhenFocused cells — others release the bottom
   *  anchor via :editing already. */
  growable?: boolean;

  /** Called on type-to-edit. Return object with cleared value and/or prevent flag.
   *  CMS-web pattern: text clears to '', number clears only for digits, date blocks non-digits.
   *  Return undefined to block the key. Return {} to enter edit mode without changing the value. */
  onTypeToEdit?: (params: {
    key: string;
    validation?: ValidationRule[];
  }) => { value?: any } | undefined;

  /** Optional hook to normalize the committed value (e.g. auto-prepend https://).
   *  Called before commitEdit() from EditableCell (blur/Enter) and useKeyboardNavigation (Tab).
   *  The value is written back to editingValue via runInAction before committing. */
  normalizeOnCommit?: (value: any) => any;

  /** When true, type-to-edit flushes the edit-mode render synchronously and
   *  focuses the input within the same keydown event, then lets the browser's
   *  default action deliver the keystroke to the now-focused input naturally
   *  (no preventDefault, no programmatic value injection).
   *  Mirrors cms-web's `focusOnTyping` pattern. Use for cells that wrap a
   *  controlled input whose value can't be seeded via props (e.g. WSR TimeInput). */
  nativeTypeToEdit?: boolean;
}

export interface CellTypeConfigMap {
  text: never;
  number: never;
  boolean: never;
  url: never;
  email: never;
  color: never;
  date: never;
  time: never;
  datetime: never;
  object: never;
  image: never;
  document: never;
  multiDocument: never;
  richContent: never;
  video: never;
  audio: never;
  richText: RichTextCellTypeConfig;
  mediaGallery: never;
  reference: ReferenceTypeConfig;
  multiReference: ReferenceTypeConfig;
  address: AddressCellTypeConfig;
  custom: Omit<CellType, 'type'>;
}

interface EditableColumnBase<T> extends Omit<TableColumn<T>, 'render'> {
  id: string;
  /** Column type — maps to a CellType in the registry */
  cellType: keyof CellTypeConfigMap;

  /** Extract the cell value from the row data */
  getValue: (item: T) => any;

  /** Return updated item with new cell value (immutable) */
  setValue?: (item: T, value: any) => T;

  /** Column-level validation rules */
  validation?: ValidationRule[];

  /** Is this column editable? (default: true if setValue exists) */
  editable?: boolean | ((item: T) => boolean);

  /** True if this column represents the schema's primary field. Drives header-action disable rules (e.g. blocks Delete, blocks Make Primary). */
  isPrimary?: boolean;

  /** True if this column represents a system field. Drives header-action disable rules (e.g. blocks Edit, Delete, Duplicate). */
  isSystem?: boolean;
}

export type EditableColumn<
  T,
  C extends keyof CellTypeConfigMap = keyof CellTypeConfigMap,
> = C extends C
  ? EditableColumnBase<T> & {
      cellType: C;
      typeConfig?: CellTypeConfigMap[C] extends never
        ? never
        : CellTypeConfigMap[C];
    }
  : never;
