import { useEffect, useRef, useState } from 'react'
import { imgUrl } from '../api'
import { isTypingTarget } from '../useEscape'

// Lightroom-style grid: fixed square cells, photos fully visible via
// object-fit: contain. Cell size depends only on the container width, so a
// photo renders identically no matter which other photos are in view.
//
// Keyboard (while `keyboard` is true, i.e. no lightbox/modal open): arrow
// keys move a cursor through the grid, Space opens the viewer on it,
// Enter toggles selection, and the page's culling `actions` (P/X/U, stars,
// S) run against the cursor photo — same table as in the lightbox.
// `externalIndex` mirrors the lightbox position into the cursor, so closing
// the viewer continues where the culling run ended.
export default function Gallery({
  photos,
  onOpen,
  overlay,
  selected,
  onToggleSelect,
  keyboard = false,
  externalIndex = -1,
  actions,
}) {
  const selecting = selected && selected.size > 0
  const containerRef = useRef(null)
  // The cursor is always a valid index; `revealed` says whether the user has
  // interacted yet. The first key press reveals the cursor where it is
  // (nothing pre-marked on a fresh page, first arrow doesn't skip a photo).
  const [cursor, setCursor] = useState(0)
  const [revealed, setRevealed] = useState(false)
  const live = useRef({})
  live.current = { cursor, revealed, photos, onOpen, onToggleSelect, actions }

  useEffect(() => {
    if (externalIndex >= 0) {
      setCursor(externalIndex)
      setRevealed(true)
    }
  }, [externalIndex])

  useEffect(() => {
    if (cursor >= photos.length) setCursor(Math.max(0, photos.length - 1))
  }, [cursor, photos.length])

  useEffect(() => {
    if (!keyboard) return
    const handler = (e) => {
      const tag = e.target.tagName
      if (isTypingTarget(e.target) || tag === 'SELECT') return
      // Focused buttons/links keep their native Space/Enter; everything else
      // still reaches the grid.
      const clickable = ['BUTTON', 'A', 'LABEL', 'INPUT'].includes(tag)
      if (clickable && (e.key === ' ' || e.key === 'Enter')) return
      if (e.metaKey || e.ctrlKey || e.altKey) return
      const s = live.current
      if (s.photos.length === 0) return
      const photo = s.photos[s.cursor]
      const el = containerRef.current
      const columns = el ? getComputedStyle(el).gridTemplateColumns.split(' ').length : 1
      const count = s.photos.length
      let next = null
      if (e.key === 'ArrowRight') next = Math.min(count - 1, s.cursor + 1)
      else if (e.key === 'ArrowLeft') next = Math.max(0, s.cursor - 1)
      else if (e.key === 'ArrowDown') next = Math.min(count - 1, s.cursor + columns)
      else if (e.key === 'ArrowUp') next = Math.max(0, s.cursor - columns)
      else if (e.key === ' ') {
        if (photo) {
          e.preventDefault()
          setRevealed(true)
          s.onOpen?.(s.cursor)
        }
        return
      } else if (e.key === 'Enter') {
        if (photo && s.onToggleSelect) {
          e.preventDefault()
          setRevealed(true)
          s.onToggleSelect(photo.id)
        }
        return
      } else {
        const key = e.key.toLowerCase()
        const action = s.actions?.find((a) => a.keys.includes(key))
        if (action && photo) {
          setRevealed(true)
          action.run(photo, key)
        }
        return
      }
      e.preventDefault()
      setRevealed(true)
      // The first arrow press only reveals the cursor; movement starts with
      // the second.
      if (s.revealed) {
        setCursor(next)
        el?.children[next]?.scrollIntoView({ block: 'nearest' })
      } else {
        el?.children[s.cursor]?.scrollIntoView({ block: 'nearest' })
      }
    }
    window.addEventListener('keydown', handler)
    return () => window.removeEventListener('keydown', handler)
  }, [keyboard])

  return (
    <div ref={containerRef} className={`gallery${selecting ? ' selecting' : ''}`}>
      {photos.map((p, index) => {
        const isSelected = selected ? selected.has(p.id) : false
        return (
          <div
            key={p.id}
            className={`g-item${isSelected ? ' selected' : ''}${revealed && index === cursor ? ' focused' : ''}`}
            onClick={() => onOpen && onOpen(index)}
          >
            <img src={imgUrl(p, 'thumb')} loading="lazy" alt={p.filename} />
            {onToggleSelect && (
              <button
                className="g-check"
                title={isSelected ? 'Deselect' : 'Select'}
                onClick={(e) => {
                  e.stopPropagation()
                  onToggleSelect(p.id, e.shiftKey)
                }}
              >
                ✓
              </button>
            )}
            {overlay && overlay(p)}
          </div>
        )
      })}
    </div>
  )
}
