import { useEffect, useRef } from 'react'
import { usePhotoStore } from '../store/photoStore'

/** A single row of the visual grid. Only the photo id is required —
 *  callers (Timeline, MemoriesView) can keep richer cell shapes but we
 *  only care about id for navigation. */
export interface GridNavRow {
  cells: { id: string }[]
}

interface UseGridKeyNavArgs {
  /** The flat, in-order grid rows the user sees. Navigation wraps at
   *  row ends and clamps to the destination row's width on vertical
   *  moves so half-full trailing rows don't land on empty cells. */
  rows: GridNavRow[]
  /** Gate the listener. Typically `viewMode === 'grid'` AND the view
   *  is mounted. When false, the window keydown listener is detached. */
  enabled: boolean
  /** Called after a successful arrow-nav move so the caller can scroll
   *  the destination row into view. No-op moves (same row, within
   *  viewport) still call it — the callback decides whether to scroll. */
  scrollRowIntoView?: (rowIdx: number) => void
}

/**
 * Shared arrow/Ctrl+A/Escape grid keyboard handler. Centralises the
 * navigation logic Timeline and MemoriesView both need so they stay in
 * lockstep — any future grid view can opt in by supplying its own
 * `rows` and (optionally) a scroll callback.
 *
 * Scroll math stays with the caller because each grid measures row
 * geometry differently (TanStack virtualizer for Timeline; native DOM
 * offsets for the non-virtualised MemoriesView).
 */
export function useGridKeyNav({
  rows,
  enabled,
  scrollRowIntoView,
}: UseGridKeyNavArgs) {
  // Read inputs through a ref so the listener binds once per `enabled`
  // toggle — without this the handler would re-attach on every render,
  // churning the window event map.
  const stateRef = useRef({ rows, scrollRowIntoView })
  stateRef.current = { rows, scrollRowIntoView }

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

    const handleKeyDown = (e: KeyboardEvent) => {
      const { rows, scrollRowIntoView } = stateRef.current
      if (rows.length === 0) return

      const target = e.target as HTMLElement | null
      if (
        target &&
        (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA')
      ) {
        return
      }

      const store = usePhotoStore.getState()

      const findActive = (): { row: number; col: number } | null => {
        const aid = store.activePhotoId
        if (!aid) return null
        for (let r = 0; r < rows.length; r++) {
          const c = rows[r].cells.findIndex((cell) => cell.id === aid)
          if (c >= 0) return { row: r, col: c }
        }
        return null
      }

      const move = (dr: number, dc: number) => {
        const current = findActive() ?? { row: 0, col: -1 }
        let nextRow = current.row
        let nextCol = current.col + dc

        if (dc !== 0) {
          // Wrap left/right across row boundaries.
          while (nextCol < 0 && nextRow > 0) {
            nextRow -= 1
            nextCol = rows[nextRow].cells.length - 1
          }
          while (
            nextRow < rows.length &&
            nextCol >= rows[nextRow].cells.length
          ) {
            if (nextRow === rows.length - 1) {
              nextCol = rows[nextRow].cells.length - 1
              break
            }
            nextRow += 1
            nextCol = 0
          }
          if (nextCol < 0) nextCol = 0
        }

        if (dr !== 0) {
          nextRow += dr
          if (nextRow < 0) nextRow = 0
          if (nextRow >= rows.length) nextRow = rows.length - 1
          // Clamp the column to the destination row's actual width so
          // moving down into a half-full row lands on its last cell
          // instead of nothing.
          const rowLen = rows[nextRow].cells.length
          if (nextCol >= rowLen) nextCol = rowLen - 1
          if (nextCol < 0) nextCol = 0
        }

        const dest = rows[nextRow]?.cells[nextCol]
        if (!dest) return

        // Read fresh actions each call so the hook stays zero-dep.
        const { selectRange, selectPhoto } = usePhotoStore.getState()
        if (e.shiftKey) selectRange(dest.id)
        else selectPhoto(dest.id)

        scrollRowIntoView?.(nextRow)
      }

      switch (e.key) {
        case 'ArrowUp':
          e.preventDefault()
          move(-1, 0)
          break
        case 'ArrowDown':
          e.preventDefault()
          move(1, 0)
          break
        case 'ArrowLeft':
          e.preventDefault()
          move(0, -1)
          break
        case 'ArrowRight':
          e.preventDefault()
          move(0, 1)
          break
        case 'a':
          if (e.ctrlKey || e.metaKey) {
            e.preventDefault()
            const { selectedPhotos, togglePhotoSelection } =
              usePhotoStore.getState()
            const selected = new Set(selectedPhotos)
            for (const row of rows) {
              for (const cell of row.cells) {
                if (!selected.has(cell.id)) togglePhotoSelection(cell.id)
              }
            }
          }
          break
        case 'Escape':
          e.preventDefault()
          usePhotoStore.getState().clearSelection()
          break
      }
    }

    window.addEventListener('keydown', handleKeyDown)
    return () => window.removeEventListener('keydown', handleKeyDown)
  }, [enabled])
}
