import * as React from 'react'

import { DataGridCellWrapper } from '@components/ui/data-grid/data-grid-cell-wrapper'
import { CellVariantProps } from './cell-variant-types'

export function ShortTextCell<TData>({
  cell,
  table,
  rowIndex,
  columnId,
  isEditing,
  isFocused,
  isSelected,
}: Readonly<CellVariantProps<TData, string>>) {
  const initialValue = cell.getValue()
  const [value, setValue] = React.useState(initialValue)
  const inputRef = React.useRef<HTMLInputElement>(null)
  const containerRef = React.useRef<HTMLDivElement>(null)
  const meta = table.options.meta
  const colMeta = cell.column.columnDef.meta
  // 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 onBlur = React.useCallback(() => {
    const currentValue = inputRef.current?.value ?? ''
    if (currentValue !== initialValue) {
      meta?.onDataUpdate?.({ rowIndex, columnId, value: currentValue })
    }
    meta?.onCellEditingStop?.()
  }, [meta, rowIndex, columnId, initialValue])

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

  const handleEditingKeyDown = React.useCallback(
    (event: React.KeyboardEvent<HTMLDivElement>) => {
      if (event.key === 'Enter') {
        event.preventDefault()
        const currentValue = inputRef.current?.value ?? ''
        if (currentValue !== initialValue) {
          meta?.onDataUpdate?.({ rowIndex, columnId, value: currentValue })
        }
        meta?.onCellEditingStop?.({ moveToNextRow: true })
      } else if (event.key === 'Tab') {
        event.preventDefault()
        const currentValue = inputRef.current?.value ?? ''
        if (currentValue !== initialValue) {
          meta?.onDataUpdate?.({ rowIndex, columnId, value: currentValue })
        }
        meta?.onCellEditingStop?.({
          direction: event.shiftKey ? 'left' : 'right',
        })
      } else if (event.key === 'Escape') {
        event.preventDefault()
        setValue(initialValue)
        inputRef.current?.blur()
      }
    },
    [initialValue, meta, rowIndex, columnId],
  )

  const handleFocusedKeyDown = React.useCallback(
    (event: React.KeyboardEvent<HTMLDivElement>) => {
      if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
        // For non-editable columns we must not change the displayed value when the user types while focused.
        if (!isEditable) {
          return
        }

        // Handle typing to pre-fill the value when editing starts
        setValue(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(() => {
    setValue(initialValue)
  }, [initialValue])

  React.useEffect(() => {
    if (isEditing && inputRef.current) {
      inputRef.current.focus()
      inputRef.current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length)
    }
    // Don't focus if we're in the middle of a scroll operation
    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="text"
          value={value ?? ''}
          onBlur={onBlur}
          onChange={onChange}
          className="size-full bg-transparent outline-none"
        />
      ) : (
        <div className="size-full overflow-hidden outline-none">{value}</div>
      )}
    </DataGridCellWrapper>
  )
}
