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

function formatTime(secs: number, t: (key: string, opts?: { count: number }) => string): string {
  const m = Math.floor(secs / 60)
  const s = secs % 60
  if (m === 0) return t('a11y.seconds', { count: s })
  if (s === 0) return t('a11y.minutes', { count: m })
  return `${t('a11y.minutes', { count: m })} ${t('a11y.seconds', { count: s })}`
}

export function useNarrator() {
  const prevCell = useRef<{ row: number; col: number } | null>(null)
  const prevDirection = useRef<string | null>(null)
  const prevEntries = useRef<string[][] | null>(null)
  const prevActiveClue = useRef<number | null>(null)
  const prevSolved = useRef(false)

  // sync settings to narrator module whenever they change
  const narratorSettings = useStore(s => s.narratorSettings)
  useEffect(() => {
    narrator.updateSettings(narratorSettings)
  }, [narratorSettings])

  // subscribe to store changes
  useEffect(() => {
    const unsub = useStore.subscribe((state, prev) => {
      if (!state.narratorEnabled || !state.puzzle) return
      const t = i18n.t
      const v = state.narratorSettings.verbosity

      // direction change
      if (state.direction !== prevDirection.current && prevDirection.current !== null) {
        narrator.speak(t('a11y.nowSolving', { dir: t(state.direction === 'across' ? 'game.across' : 'game.down') }))
      }
      prevDirection.current = state.direction

      // cell selection
      if (state.selectedCell &&
          (state.selectedCell.row !== prevCell.current?.row ||
           state.selectedCell.col !== prevCell.current?.col)) {

        const { row, col } = state.selectedCell
        const letter = state.entries[row]?.[col] || ''
        const cellContent = letter ? letter : t('a11y.empty')

        if (v === 'brief') {
          narrator.speak(`Row ${row + 1}, Column ${col + 1}`)
        } else if (v === 'normal') {
          narrator.speak(`Row ${row + 1}, Column ${col + 1}, ${cellContent}, ${state.direction}`)
        } else {
          const puzzle = state.puzzle
          let neighbors = ''
          if (col > 0 && puzzle.cells[row][col - 1] !== 'black') {
            const l = state.entries[row][col - 1]
            neighbors += l ? `${l} to the left` : 'blank to the left'
          }
          if (row > 0 && puzzle.cells[row - 1]?.[col] !== 'black') {
            const l = state.entries[row - 1]?.[col]
            if (neighbors) neighbors += ', '
            neighbors += l ? `${l} above` : 'blank above'
          }
          let msg = `Row ${row + 1}, Column ${col + 1}, ${cellContent}, ${state.direction}`
          if (neighbors) msg += `. Neighboring: ${neighbors}`
          narrator.speak(msg)
        }
      }
      prevCell.current = state.selectedCell

      // letter entry - detect new letter in the grid
      if (prevEntries.current && state.entries !== prevEntries.current) {
        for (let r = 0; r < state.entries.length; r++) {
          for (let c = 0; c < state.entries[r].length; c++) {
            const cur = state.entries[r][c]
            const old = prevEntries.current[r]?.[c] || ''
            if (cur && !old) {
              if (v === 'brief') narrator.speak(cur, false)
              else if (v === 'normal') narrator.speak(t('a11y.entered', { letter: cur }), false)
              else narrator.speak(t('a11y.enteredAt', { letter: cur, row: r + 1, col: c + 1 }), false)

              // check word completion
              const span = state.wordSpans.find(s =>
                s.cells.some(([sr, sc]) => sr === r && sc === c)
              )
              if (span) {
                const allFilled = span.cells.every(([wr, wc]) => state.entries[wr]?.[wc])
                if (allFilled) {
                  const wasFilled = span.cells.every(([wr, wc]) => prevEntries.current?.[wr]?.[wc])
                  if (!wasFilled) {
                    const word = span.cells.map(([wr, wc]) => state.entries[wr][wc]).join('')
                    setTimeout(() => narrator.speak(t('a11y.wordComplete', { word: word.split('').join(' ') }), false), 300)
                  }
                }
              }
            }
          }
        }
      }
      prevEntries.current = state.entries

      // clue focus change
      if (state.activeClue !== null && state.activeClue !== prevActiveClue.current) {
        const clues = state.direction === 'across' ? state.puzzle.clues_across : state.puzzle.clues_down
        const clue = clues.find(c => c.number === state.activeClue)
        if (clue) {
          const span = state.wordSpans.find(s => s.number === state.activeClue && s.direction === state.direction)
          const len = span?.cells.length || 0
          const filled = span ? span.cells.filter(([r, c]) => state.entries[r]?.[c]).length : 0

          if (v === 'brief') {
            narrator.speak(`${state.activeClue} ${t(state.direction === 'across' ? 'game.across' : 'game.down')}: ${clue.text}`, false)
          } else if (v === 'normal') {
            narrator.speak(t('a11y.clueNormal', { number: state.activeClue, dir: t(state.direction === 'across' ? 'game.across' : 'game.down'), length: len, text: clue.text }), false)
          } else {
            narrator.speak(t('a11y.clueVerbose', { number: state.activeClue, dir: t(state.direction === 'across' ? 'game.across' : 'game.down'), length: len, filled, text: clue.text }), false)
          }
        }
      }
      prevActiveClue.current = state.activeClue

      // hint reveal - detect newly revealed cells
      if (prev.cellStatus !== state.cellStatus && prevEntries.current) {
        for (let r = 0; r < state.cellStatus.length; r++) {
          for (let c = 0; c < state.cellStatus[r].length; c++) {
            if (state.cellStatus[r][c] === 'revealed' && prev.cellStatus[r]?.[c] !== 'revealed') {
              const letter = state.entries[r][c]
              const span = state.wordSpans.find(s =>
                s.cells.some(([sr, sc]) => sr === r && sc === c)
              )
              const pos = span ? span.cells.findIndex(([sr, sc]) => sr === r && sc === c) + 1 : 0
              if (letter && pos) {
                narrator.speak(t('a11y.revealed', { letter, pos }), false)
              }
            }
          }
        }
      }

      // puzzle solved
      if (state.solved && !prevSolved.current) {
        const time = formatTime(state.elapsed, t)
        narrator.speak(t('a11y.solvedTime', { time }))
      }
      prevSolved.current = state.solved
    })

    return unsub
  }, [])
}
