import { FC, useCallback, useState } from 'react'
import { AnyOperation } from '../../../../../types/change'
import {
  useCodeMirrorStateContext,
  useCodeMirrorViewContext,
} from '@/features/source-editor/components/codemirror-editor'
import { isSelectionWithinOp } from '../utils/is-selection-within-op'
import { EditorSelection } from '@codemirror/state'
import { EditorView } from '@codemirror/view'
import classNames from 'classnames'
import { highlightRanges } from '@/features/source-editor/extensions/ranges'

export const ReviewPanelEntry: FC<{
  position: number
  op: AnyOperation
  top?: number
  className?: string
  selectLineOnFocus?: boolean
}> = ({ children, position, top, op, className, selectLineOnFocus }) => {
  const state = useCodeMirrorStateContext()
  const view = useCodeMirrorViewContext()
  const [focused, setFocused] = useState(false)

  const highlighted = isSelectionWithinOp(op, state.selection.main)

  const focusHandler = useCallback(() => {
    setTimeout(() => {
      // without setTimeout, error "EditorView.update are not allowed while an update is in progress" can occur
      // this can be avoided by using onClick rather than onFocus but it will then not pick up <Tab> or <Shift+Tab> events for focusing entries
      if (selectLineOnFocus) {
        view.dispatch({
          selection: EditorSelection.cursor(position),
          effects: EditorView.scrollIntoView(position, { y: 'center' }),
        })
      }
    }, 0)
    setFocused(true)
  }, [view, position, selectLineOnFocus])

  return (
    <div
      onFocus={focusHandler}
      onBlur={() => setFocused(false)}
      onMouseEnter={() => view.dispatch(highlightRanges(op))}
      onMouseLeave={() => view.dispatch(highlightRanges())}
      role="button"
      tabIndex={position + 1}
      className={classNames(
        'review-panel-entry',
        {
          'review-panel-entry-focused': focused,
          'review-panel-entry-highlighted': highlighted,
        },
        className
      )}
      data-top={top}
      data-pos={position}
      style={{
        position: top === undefined ? 'relative' : 'absolute',
        visibility: top === undefined ? 'visible' : 'hidden',
        transition: 'top .3s, left .1s, right .1s',
      }}
    >
      {children}
    </div>
  )
}
