import { RowData } from '@tanstack/react-table'

import { ISODateRange } from './cell-variants/utils/gantt-timerange-picker'

export type RowHeightValue = 'short' | 'medium' | 'tall' | 'extra-tall'

export type CellSelectOption = {
  label: string
  value: string
}

export type Cell =
  | {
      variant: 'short-text'
    }
  | {
      variant: 'long-text'
    }
  | {
      variant: 'number'
      min?: number
      max?: number
      step?: number
      prefix?: string
      suffix?: string
      fallbackValue?: string
    }
  | {
      variant: 'select'
      options: CellSelectOption[]
      hasSearch?: boolean
    }
  | {
      variant: 'multi-select'
      options: CellSelectOption[]
    }
  | {
      variant: 'checkbox'
    }
  | {
      variant: 'date'
    }
  | {
      variant: 'react-node'
    }
  | {
      variant: 'gantt'
      timelineStart: Date
      timelineEnd: Date
      dateRangeFrom?: Date
      dateRangeTo?: Date
      onRangeChange?: (values: ISODateRange | undefined) => void
    }

export type UpdateCell = {
  rowIndex: number
  columnId: string
  value: unknown
}

declare module '@tanstack/react-table' {
  // biome-ignore lint/correctness/noUnusedVariables lint/correctness/noUnusedVariables: TData and TValue are used in the ColumnMeta interface
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-unused-vars
  interface ColumnMeta<TData extends RowData, TValue> {
    label?: string
    cell?: Cell
    align?: 'left' | 'right' | 'center'
    className?: string | ((row: TData) => string | undefined)
    editable?: boolean | ((row: TData) => boolean)
  }
  // biome-ignore lint/correctness/noUnusedVariables: TData is used in the TableMeta interface
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-unused-vars
  interface TableMeta<TData extends RowData> {
    dataGridRef?: React.RefObject<HTMLElement | null>
    focusedCell?: CellPosition | null
    editingCell?: CellPosition | null
    selectionState?: SelectionState
    searchOpen?: boolean
    isScrolling?: boolean
    getIsCellSelected?: (rowIndex: number, columnId: string) => boolean
    getIsSearchMatch?: (rowIndex: number, columnId: string) => boolean
    getIsActiveSearchMatch?: (rowIndex: number, columnId: string) => boolean
    onDataUpdate?: (props: UpdateCell | UpdateCell[]) => void
    /**
     * Called for each individual cell update. Receives a single UpdateCell.
     * Use this to implement field-level updates (id + column + value).
     */
    onCellChange?: (update: UpdateCell) => void
    onRowsDelete?: (rowIndices: number[]) => void | Promise<void>
    onColumnClick?: (columnId: string) => void
    onCellClick?: (rowIndex: number, columnId: string, event?: React.MouseEvent) => void
    onCellDoubleClick?: (rowIndex: number, columnId: string) => void
    onCellMouseDown?: (rowIndex: number, columnId: string, event: React.MouseEvent) => void
    onCellMouseEnter?: (rowIndex: number, columnId: string, event: React.MouseEvent) => void
    onCellMouseUp?: () => void
    onCellContextMenu?: (rowIndex: number, columnId: string, event: React.MouseEvent) => void
    onCellEditingStart?: (rowIndex: number, columnId: string) => void
    onCellEditingStop?: (opts?: { direction?: NavigationDirection; moveToNextRow?: boolean }) => void
    contextMenu?: ContextMenuState
    onContextMenuOpenChange?: (open: boolean) => void
    rowHeight?: RowHeightValue
    onRowHeightChange?: (value: RowHeightValue) => void
    onRowSelect?: (rowIndex: number, checked: boolean, shiftKey: boolean) => void
  }
}

export type CellPosition = {
  rowIndex: number
  columnId: string
}

export type CellRange = {
  start: CellPosition
  end: CellPosition
}

export type SelectionState = {
  selectedCells: Set<string>
  selectionRange: CellRange | null
  isSelecting: boolean
}

export type ContextMenuState = {
  open: boolean
  x: number
  y: number
}

export type NavigationDirection =
  | 'up'
  | 'down'
  | 'left'
  | 'right'
  | 'home'
  | 'end'
  | 'ctrl+home'
  | 'ctrl+end'
  | 'pageup'
  | 'pagedown'

export type SearchState = {
  searchMatches: CellPosition[]
  matchIndex: number
  searchOpen: boolean
  onSearchOpenChange: (open: boolean) => void
  searchQuery: string
  onSearchQueryChange: (query: string) => void
  onSearch: (query: string) => void
  onNavigateToNextMatch: () => void
  onNavigateToPrevMatch: () => void
}
