import { useEffect } from 'react'
import { useStore } from '../store'
import * as narrator from '../narrator'

function readCurrentCell() {
  const { puzzle, selectedCell, entries, direction, wordSpans, activeClue } = useStore.getState()
  if (!puzzle || !selectedCell) {
    narrator.speak('No cell selected')
    return
  }
  const { row, col } = selectedCell
  const letter = entries[row]?.[col] || ''
  const num = puzzle.cell_numbers[`${row},${col}`]
  const span = wordSpans.find(s => s.number === activeClue && s.direction === direction)
  const pos = span ? span.cells.findIndex(([r, c]) => r === row && c === col) + 1 : 0
  const len = span?.cells.length || 0

  let msg = `Row ${row + 1}, Column ${col + 1}`
  if (num) msg += `, number ${num}`
  msg += letter ? `, letter ${letter}` : ', empty'
  msg += `, ${direction}`
  if (pos && len) msg += `, position ${pos} of ${len}`
  narrator.speak(msg)
}

function readCurrentClue() {
  const { puzzle, activeClue, direction, wordSpans, entries, narratorSettings } = useStore.getState()
  if (!puzzle || activeClue === null) {
    narrator.speak('No clue selected')
    return
  }
  const clues = direction === 'across' ? puzzle.clues_across : puzzle.clues_down
  const clue = clues.find(c => c.number === activeClue)
  if (!clue) return

  const span = wordSpans.find(s => s.number === activeClue && s.direction === direction)
  const len = span?.cells.length || 0
  const filled = span ? span.cells.filter(([r, c]) => entries[r]?.[c]).length : 0
  const v = narratorSettings.verbosity

  if (v === 'brief') {
    narrator.speak(`${activeClue} ${direction}: ${clue.text}`)
  } else if (v === 'normal') {
    narrator.speak(`${activeClue} ${direction}, ${len} letters: ${clue.text}`)
  } else {
    narrator.speak(`${activeClue} ${direction}, ${len} letters, ${filled} filled: ${clue.text}`)
  }
}

function readPuzzleOverview() {
  const { puzzle, entries, wordSpans } = useStore.getState()
  if (!puzzle) {
    narrator.speak('No puzzle loaded')
    return
  }
  let totalCells = 0
  let filledCells = 0
  for (let r = 0; r < puzzle.height; r++) {
    for (let c = 0; c < puzzle.width; c++) {
      if (puzzle.cells[r][c] !== 'black') {
        totalCells++
        if (entries[r]?.[c]) filledCells++
      }
    }
  }
  const totalClues = puzzle.clues_across.length + puzzle.clues_down.length
  const completedWords = wordSpans.filter(s =>
    s.cells.every(([r, c]) => entries[r]?.[c])
  ).length
  const remaining = totalClues - completedWords

  narrator.speak(
    `${puzzle.width} by ${puzzle.height} puzzle, ${filledCells} of ${totalCells} cells filled, ${remaining} clues remaining`
  )
}

function getGridEl(): HTMLElement | null {
  return document.querySelector('.grid-wrapper')
}

// tracks whether focus (or the last pointer interaction) is inside the grid,
// so typing after clicking a header button does not fill grid cells
let gridHasFocus = false

function updateGridFocus(target: EventTarget | null) {
  gridHasFocus = target instanceof Node && !!getGridEl()?.contains(target)
}

export function useKeyboard() {
  const puzzle = useStore((s) => s.puzzle)
  const typeLetter = useStore((s) => s.typeLetter)
  const deleteLetter = useStore((s) => s.deleteLetter)
  const moveSelection = useStore((s) => s.moveSelection)
  const toggleDirection = useStore((s) => s.toggleDirection)
  const tabClue = useStore((s) => s.tabClue)
  const narratorEnabled = useStore((s) => s.narratorEnabled)
  const undo = useStore((s) => s.undo)
  const redo = useStore((s) => s.redo)

  useEffect(() => {
    if (!puzzle) return

    gridHasFocus = !!getGridEl()?.contains(document.activeElement)

    const onPointerDown = (e: PointerEvent) => updateGridFocus(e.target)
    const onFocusIn = (e: FocusEvent) => updateGridFocus(e.target)
    document.addEventListener('pointerdown', onPointerDown, true)
    document.addEventListener('focusin', onFocusIn)

    // give the grid initial keyboard focus when nothing else is focused
    if (document.activeElement === document.body || document.activeElement === null) {
      const svg = getGridEl()?.querySelector('svg')
      if (svg instanceof SVGSVGElement) svg.focus()
    }

    function onKeyDown(e: KeyboardEvent) {
      if (
        e.target instanceof HTMLInputElement ||
        e.target instanceof HTMLTextAreaElement ||
        e.target instanceof HTMLSelectElement
      )
        return

      // only handle letter input when the grid area has focus
      const gridFocused = gridHasFocus || !!getGridEl()?.contains(document.activeElement)

      const key = e.key
      const narr = useStore.getState().narratorEnabled

      // narrator-only shortcuts - only when grid is focused
      if (narr && gridFocused) {
        if (key === ' ') {
          e.preventDefault()
          readCurrentCell()
          return
        }
        if (key === '?' || key === '/') {
          e.preventDefault()
          readCurrentClue()
          return
        }
        if ((e.ctrlKey && key.toLowerCase() === 'h') || key === 'F1') {
          e.preventDefault()
          readPuzzleOverview()
          return
        }
        if (key === 'Tab') {
          e.preventDefault()
          tabClue(!e.shiftKey)
          // clue announcement handled by useNarrator hook
          return
        }
      }

      if (gridFocused && e.ctrlKey && key.toLowerCase() === 'z' && !e.shiftKey) {
        e.preventDefault()
        undo()
        return
      }
      if (gridFocused && ((e.ctrlKey && key.toLowerCase() === 'y') || (e.ctrlKey && e.shiftKey && key.toLowerCase() === 'z'))) {
        e.preventDefault()
        redo()
        return
      }

      if (gridFocused && key.length === 1 && key >= 'a' && key <= 'z') {
        e.preventDefault()
        typeLetter(key)
        return
      }
      if (gridFocused && key.length === 1 && key >= 'A' && key <= 'Z') {
        e.preventDefault()
        typeLetter(key)
        return
      }

      if (!gridFocused) return

      switch (key) {
        case 'Backspace':
          e.preventDefault()
          deleteLetter()
          break
        case 'Escape':
          e.preventDefault()
          useStore.getState().deselectCell()
          break
        case 'ArrowUp':
          e.preventDefault()
          moveSelection(-1, 0)
          break
        case 'ArrowDown':
          e.preventDefault()
          moveSelection(1, 0)
          break
        case 'ArrowLeft':
          e.preventDefault()
          moveSelection(0, -1)
          break
        case 'ArrowRight':
          e.preventDefault()
          moveSelection(0, 1)
          break
        case ' ':
          e.preventDefault()
          toggleDirection()
          break
        case 'Tab':
          e.preventDefault()
          tabClue(!e.shiftKey)
          break
      }
    }

    document.addEventListener('keydown', onKeyDown)
    return () => {
      document.removeEventListener('keydown', onKeyDown)
      document.removeEventListener('pointerdown', onPointerDown, true)
      document.removeEventListener('focusin', onFocusIn)
    }
  }, [puzzle, typeLetter, deleteLetter, moveSelection, toggleDirection, tabClue, narratorEnabled, undo, redo])
}
