import type { ColumnMeta } from '@tanstack/react-table'

import * as React from 'react'
import { DataGridCellWrapper } from '@components/ui/data-grid/data-grid-cell-wrapper'
import { cn } from '@lib/utils'
import { CellVariantProps } from './cell-variant-types'

export function NumberCell<TData>({
  cell,
  table,
  rowIndex,
  columnId,
  isFocused,
  isEditing,
  isSelected,
}: Readonly<CellVariantProps<TData, number>>) {
  const initialValue = cell.getValue() as number | null
  const inputRef = React.useRef<HTMLInputElement>(null)
  const containerRef = React.useRef<HTMLDivElement>(null)
  const meta = table.options.meta
  const colMeta = cell.column.columnDef.meta as ColumnMeta<TData, number>
  const cellOptions = colMeta?.cell
  // Normalize editable to a resolver function and memoize to avoid typeof checks per cell
  const editableResolver = React.useMemo(() => {
    const v = colMeta?.editable
    if (v === undefined) {
      return () => true
    }
    return typeof v === 'function' ? (v as (row: TData) => boolean) : () => Boolean(v)
  }, [colMeta?.editable])
  const isEditable = editableResolver(cell.row.original)
  const {
    min,
    max,
    step,
    prefix = '',
    suffix = '',
    fallbackValue = '',
  } = cellOptions?.variant === 'number' ? cellOptions : {}

  // Keep an unformatted string for editing (no grouping separators)
  const [editValue, setEditValue] = React.useState<string>(
    initialValue !== null && initialValue !== undefined ? String(initialValue) : '',
  )

  const onBlur = React.useCallback(() => {
    const numValue = editValue === '' ? null : Number(editValue)
    if (numValue !== initialValue) {
      meta?.onDataUpdate?.({ rowIndex, columnId, value: numValue })
    }
    meta?.onCellEditingStop?.()
  }, [meta, rowIndex, columnId, initialValue, editValue])

  const onChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
    setEditValue(event.target.value)
  }, [])

  const parseNumValue = React.useCallback((): number | null => {
    return editValue === '' ? null : Number(editValue)
  }, [editValue])

  const saveAndStop = React.useCallback(
    (options?: { moveToNextRow?: boolean; direction?: 'left' | 'right' }) => {
      const numValue = parseNumValue()
      if (numValue !== initialValue) {
        meta?.onDataUpdate?.({ rowIndex, columnId, value: numValue })
      }
      meta?.onCellEditingStop?.(options)
    },
    [parseNumValue, initialValue, meta, rowIndex, columnId],
  )

  const handleEditingKeyDown = React.useCallback(
    (event: React.KeyboardEvent<HTMLDivElement>) => {
      if (event.key === 'Enter') {
        event.preventDefault()
        saveAndStop({ moveToNextRow: true })
      } else if (event.key === 'Tab') {
        event.preventDefault()
        saveAndStop({ direction: event.shiftKey ? 'left' : 'right' })
      } else if (event.key === 'Escape') {
        event.preventDefault()
        setEditValue(initialValue !== null && initialValue !== undefined ? String(initialValue) : '')
        inputRef.current?.blur()
      }
    },
    [saveAndStop, initialValue],
  )

  const handleFocusedKeyDown = React.useCallback(
    (event: React.KeyboardEvent<HTMLDivElement>) => {
      if (!isEditable) return

      if (event.key === 'Backspace') {
        setEditValue('')
      } else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
        setEditValue(event.key)
      }
    },
    [isEditable],
  )

  const onWrapperKeyDown = React.useCallback(
    (event: React.KeyboardEvent<HTMLDivElement>) => {
      if (isEditing) {
        handleEditingKeyDown(event)
      } else if (isFocused) {
        handleFocusedKeyDown(event)
      }
    },
    [isEditing, isFocused, handleEditingKeyDown, handleFocusedKeyDown],
  )

  React.useEffect(() => {
    setEditValue(initialValue !== null && initialValue !== undefined ? String(initialValue) : '')
  }, [initialValue])

  React.useEffect(() => {
    if (isEditing && inputRef.current) {
      inputRef.current.focus()
      inputRef.current.select()
    }
    if (isFocused && !isEditing && !meta?.searchOpen && !meta?.isScrolling && containerRef.current) {
      containerRef.current.focus()
    }
  }, [isFocused, isEditing, meta?.searchOpen, meta?.isScrolling])

  return (
    <DataGridCellWrapper
      ref={containerRef}
      cell={cell}
      table={table}
      rowIndex={rowIndex}
      columnId={columnId}
      isEditing={isEditing}
      isFocused={isFocused}
      isSelected={isSelected}
      onKeyDown={onWrapperKeyDown}
    >
      {isEditing ? (
        <input
          ref={inputRef}
          type="number"
          value={editValue}
          min={min}
          max={max}
          step={step}
          onBlur={onBlur}
          onChange={onChange}
          className="w-full border-none bg-transparent p-0 outline-none"
        />
      ) : (
        <span data-slot="grid-cell-content" className={cn('tabular-nums', colMeta?.align ?? 'text-right')}>
          {initialValue === null || initialValue === undefined || editValue === '' ? (
            fallbackValue
          ) : (
            <>
              {prefix}
              {initialValue.toLocaleString()}
              {suffix}
            </>
          )}
        </span>
      )}
    </DataGridCellWrapper>
  )
}
