import React, { useState, useMemo, useCallback, useEffect, useLayoutEffect, useRef } from 'react'
import type { ItemStack, RecipeGuide, RecipeNavFrame, BlockTextureRender } from '../../types'
import { useScale } from '../../context/ScaleContext'
import { useInventoryContext } from '../../context/InventoryContext'
import { useSlashFocusInput } from '../../hooks/useFocusInputShortcut'
import { Slot } from '../Slot'
import { StarIcon } from './StarIcon'
import styles from './JEI.module.css'

export interface JEIItem {
  type: number
  name: string
  displayName: string
  count?: number
  metadata?: number
  texture?: string
  blockTexture?: BlockTextureRender
}

interface JEIProps {
  items?: JEIItem[]
  position?: 'left' | 'right'
  /** Override number of item columns (default: ITEMS_PER_ROW=6). Set by InventoryOverlay from slot-grid calc. */
  cols?: number
  /** Override panel width in px. If omitted, derived from cols. */
  width?: number
  /** Max height in px for the panel. Controls rows visible. */
  maxHeight?: number
  onItemClick?: (item: JEIItem) => void
  onItemMiddleClick?: (item: JEIItem) => void
  onItemRightClick?: (item: JEIItem) => void
  /** Called when R is pressed while hovering an item — return list of recipes to display */
  onGetRecipes?: (item: JEIItem) => RecipeGuide[]
  /** Called when U is pressed while hovering an item — return list of usages to display */
  onGetUsages?: (item: JEIItem) => RecipeGuide[]
  /** Called when R/U shortcut should push a new recipe frame (managed externally by InventoryOverlay) */
  onPushRecipeFrame?: (frame: RecipeNavFrame) => void
  className?: string
  style?: React.CSSProperties
}

const ITEMS_PER_ROW = 6
const PADDING = 4
const FAV_KEY = 'mc-inv-jei-favorites'

function getItemKey(item: JEIItem): string {
  return `${item.type}:${item.metadata ?? 0}:${item.name}`
}

function loadFavorites(): Set<string> {
  try {
    const raw = localStorage.getItem(FAV_KEY)
    return new Set(raw ? (JSON.parse(raw) as string[]) : [])
  } catch {
    return new Set()
  }
}

function saveFavorites(favs: Set<string>) {
  try {
    localStorage.setItem(FAV_KEY, JSON.stringify([...favs]))
  } catch {}
}

export function JEI({
  items = [],
  position = 'right',
  cols: colsProp,
  width,
  maxHeight,
  onItemClick,
  onItemMiddleClick,
  onItemRightClick,
  onGetRecipes,
  onGetUsages,
  onPushRecipeFrame,
  className,
  style,
}: JEIProps) {
  const { scale, contentSize } = useScale()
  const { hoveredSlot } = useInventoryContext()
  const [search, setSearch] = useState('')
  const [page, setPage] = useState(0)
  const [showAllItems, setShowAllItems] = useState(false)
  const [showFavorites, setShowFavorites] = useState(false)
  const [favorites, setFavorites] = useState<Set<string>>(loadFavorites)
  // Map from negative slot index → JEI item (to enable F-key and R/U on hover)
  const slotToItemRef = useRef<Map<number, JEIItem>>(new Map())

  // Self-measured dimensions so the panel can be width:100% and fill its container
  const rootRef = useRef<HTMLDivElement>(null)
  const gridRef = useRef<HTMLDivElement>(null)
  const searchInputRef = useRef<HTMLInputElement>(null)
  useSlashFocusInput(searchInputRef, true)
  const [measuredCols, setMeasuredCols] = useState(ITEMS_PER_ROW)
  const [measuredRows, setMeasuredRows] = useState(5)

  useLayoutEffect(() => {
    const root = rootRef.current
    const grid = gridRef.current
    if (!root || !grid) return

    // Track latest sizes across both entries
    const sizes = { rootW: 0, gridH: 0 }

    const ro = new ResizeObserver((entries) => {
      for (const entry of entries) {
        // console.log('got size', entry.target.className, entry.contentRect.width, entry.contentRect.height)
        if (entry.target === (root as unknown as Element)) {
          sizes.rootW = entry.contentRect.width
        } else if (entry.target === (grid as unknown as Element)) {
          sizes.gridH = entry.contentRect.height
        }
      }
      const innerPad = PADDING * scale * 2
      setMeasuredCols(Math.max(1, Math.floor((sizes.rootW - innerPad) / contentSize)))
      setMeasuredRows(Math.max(1, Math.floor(sizes.gridH / contentSize)))
    })

    ro.observe(root as unknown as Element)
    ro.observe(grid as unknown as Element)
    return () => ro.disconnect()
  }, [scale, contentSize])

  const pushRecipeFrame = useCallback((targetItem: JEIItem, mode: 'recipes' | 'usages') => {
    const getter = mode === 'recipes' ? onGetRecipes : onGetUsages
    if (!getter || !onPushRecipeFrame) return
    const guides = getter(targetItem)
    if (guides.length === 0) return
    onPushRecipeFrame({ item: targetItem, mode, guides, guideIndex: 0 })
  }, [onGetRecipes, onGetUsages, onPushRecipeFrame])

  const padding = PADDING * scale
  const itemSize = contentSize
  // cols: explicit prop > internal measurement > default
  const cols = colsProp ?? measuredCols
  // panelWidth: explicit prop > 100% (container controls width)
  const panelWidth = width ?? '100%'
  // rows: explicit maxHeight override > internal measurement
  const rows = maxHeight ? Math.max(1, Math.floor(maxHeight / itemSize)) : measuredRows
  const itemsPerPage = cols * rows

  const toggleFavorite = useCallback((key: string) => {
    setFavorites((prev) => {
      const next = new Set(prev)
      if (next.has(key)) next.delete(key)
      else next.add(key)
      saveFavorites(next)
      return next
    })
  }, [])

  // F / R / U keyboard handlers
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      // Shift+F: toggle favorites-only filter
      if (e.code === 'KeyF' && e.shiftKey) {
        setShowFavorites((v) => !v)
        setPage(0)
        return
      }
      // F: toggle favorite for hovered JEI item
      if (e.code === 'KeyF' && hoveredSlot !== null && hoveredSlot < 0) {
        const item = slotToItemRef.current.get(hoveredSlot)
        if (item) toggleFavorite(getItemKey(item))
        return
      }

      // R / U: show recipes / usages for hovered JEI item slot
      if (e.code !== 'KeyR' && e.code !== 'KeyU') return
      const mode = e.code === 'KeyR' ? 'recipes' : 'usages'

      if (hoveredSlot !== null && hoveredSlot < 0) {
        const item = slotToItemRef.current.get(hoveredSlot)
        if (item) pushRecipeFrame(item, mode)
      }
    }
    window.addEventListener('keydown', handler)
    return () => window.removeEventListener('keydown', handler)
  }, [hoveredSlot, toggleFavorite, pushRecipeFrame])

  // Expose hoveredSlot info for parent-level nested navigation
  // (RecipeInventoryView handles its own R/U, JEI only needs to handle its own items)

  const baseList = useMemo(() => {
    if (showFavorites) return items.filter((i) => favorites.has(getItemKey(i)))
    return items
  }, [items, showFavorites, favorites])

  const filteredItems = useMemo(() => {
    if (!search.trim()) return showAllItems ? baseList : []
    const q = search.toLowerCase()
    return baseList.filter(
      (item) =>
        item.displayName.toLowerCase().includes(q) ||
        item.name.toLowerCase().includes(q) ||
        String(item.type).includes(q),
    )
  }, [baseList, search, showAllItems])

  const totalPages = Math.ceil(filteredItems.length / itemsPerPage)
  const visibleItems = filteredItems.slice(page * itemsPerPage, (page + 1) * itemsPerPage)

  const handleSearchChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    setSearch(e.target.value)
    setPage(0)
  }, [])

  const handleWheel = useCallback(
    (e: WheelEvent) => {
      e.preventDefault()
      setPage((prev) => {
        if (e.deltaY > 0) return Math.min(prev + 1, totalPages - 1)
        return Math.max(prev - 1, 0)
      })
    },
    [totalPages],
  )

  // Non-passive wheel listener so preventDefault() actually suppresses page scroll.
  useEffect(() => {
    const el = gridRef.current
    if (!el) return
    el.addEventListener('wheel', handleWheel, { passive: false })
    return () => el.removeEventListener('wheel', handleWheel)
  }, [handleWheel])

  // Build slot→item map on each render
  slotToItemRef.current.clear()
  visibleItems.forEach((jeiItem, i) => {
    const slotIndex = -(page * itemsPerPage + i + 1)
    slotToItemRef.current.set(slotIndex, jeiItem)
  })

  const displayAllButton = !showAllItems ? (
    <button
      type="button"
      className={styles.displayAllBtn}
      onClick={() => {
        setShowAllItems(true)
        setPage(0)
      }}
      style={{
        fontSize: 8 * scale,
        padding: `${2 * scale}px ${5 * scale}px`,
        fontFamily: "'Minecraftia', 'Minecraft', monospace",
      }}
    >
      Show All {baseList.length} {baseList.length === 1 ? 'item' : 'items'}
    </button>
  ) : null

  const showDisplayAllInGrid = !showAllItems && visibleItems.length === 0

  return (
    <div
      ref={rootRef}
      className={['mc-inv-root', styles.jei, className].filter(Boolean).join(' ')}
      style={{
        width: panelWidth,
        maxHeight: maxHeight ?? undefined,
        display: 'flex',
        flexDirection: 'column',
        overflow: 'hidden',
        ...style,
      }}
    >
      {/* Search + pagination controls — background only here */}
      <div
        className="mc-inv-jei-header"
        style={{
          padding: `${padding}px`,
          flexShrink: 0,
        }}
      >
        <input
          ref={searchInputRef}
          type="text"
          value={search}
          onChange={handleSearchChange}
          placeholder="Search items…"
          className={styles.searchInput}
          style={{
            width: '100%',
            padding: `${2 * scale}px`,
            fontSize: 6 * scale,
            background: '#8b8b8b',
            border: `${scale}px solid #373737`,
            color: '#ffffff',
            fontFamily: "'Minecraftia', 'Minecraft', monospace",
            outline: 'none',
            boxSizing: 'border-box',
          }}
        />
        {/* Prev / Next / Favorites below search bar */}
        <div
          className="mc-inv-jei-pagination"
          style={{
            display: 'flex',
            alignItems: 'center',
            gap: scale,
            marginTop: 2 * scale,
            fontSize: 6 * scale,
            fontFamily: "'Minecraftia', 'Minecraft', monospace",
          }}
        >
          <button
            className={styles.pageBtn}
            onClick={() => setPage((p) => (p > 0 ? p - 1 : Math.max(0, totalPages - 1)))}
            style={{ fontSize: 6 * scale, padding: `${scale}px ${2 * scale}px` }}
            title="Previous page"
          >
            ◀
          </button>
          <span className="mc-inv-jei-page-counter" style={{ flex: 1, textAlign: 'center', color: '#ffffff' }}>
            {page + 1} / {Math.max(1, totalPages)}
          </span>
          <button
            className={styles.pageBtn}
            onClick={() => setPage((p) => (p < totalPages - 1 ? p + 1 : 0))}
            style={{ fontSize: 6 * scale, padding: `${scale}px ${2 * scale}px` }}
            title="Next page"
          >
            ▶
          </button>
          <button
            className={[styles.pageBtn, showFavorites ? styles.pageBtnActive : ''].join(' ')}
            onClick={() => { setShowFavorites((v) => !v); setPage(0) }}
            style={{ fontSize: 6 * scale, padding: `${scale}px ${2 * scale}px` }}
            title={showFavorites ? 'Show all items' : 'Show favorites (F to toggle)'}
          >
            <StarIcon size={Math.max(1, Math.round(6 * scale))} />
          </button>
        </div>
      </div>

      {/* Item grid — no slot backgrounds, just raw items */}
      <div
        ref={gridRef}
        className="mc-inv-jei-grid"
        style={{
          position: 'relative',
          display: 'flex',
          flexWrap: 'wrap',
          padding: `${padding}px`,
          gap: 0,
          flex: 1,
          minHeight: 0,
          alignContent: 'flex-start',
          overflowY: 'hidden',
        }}
      >
        {showDisplayAllInGrid && displayAllButton && (
          <div
            style={{
              position: 'absolute',
              inset: 0,
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              pointerEvents: 'none',
              padding: `${padding}px`,
            }}
          >
            <span style={{ pointerEvents: 'auto' }}>{displayAllButton}</span>
          </div>
        )}
        {visibleItems.map((jeiItem, i) => {
          const itemStack: ItemStack = {
            type: jeiItem.type,
            name: jeiItem.name,
            count: jeiItem.count ?? 1,
            metadata: jeiItem.metadata,
            displayName: jeiItem.displayName,
            texture: jeiItem.texture,
            blockTexture: jeiItem.blockTexture,
          }
          const slotIndex = -(page * itemsPerPage + i + 1)
          const isFav = favorites.has(getItemKey(jeiItem))
          return (
            <div
              key={`${jeiItem.type}-${jeiItem.metadata ?? 0}-${i}`}
              className="mc-inv-jei-item-wrapper"
              style={{ position: 'relative' }}
            >
              <Slot
                index={slotIndex}
                item={itemStack}
                size={itemSize}
                noBackground
                onClickOverride={(button, mode) => {
                  if (button === 'left' && mode === 'normal') {
                    if (onItemClick) {
                      onItemClick(jeiItem)
                    } else {
                      pushRecipeFrame(jeiItem, 'recipes')
                    }
                  } else if (button === 'right' && mode === 'normal') {
                    if (onItemRightClick) {
                      onItemRightClick(jeiItem)
                    } else {
                      pushRecipeFrame(jeiItem, 'usages')
                    }
                  } else if (button === 'middle' && onItemMiddleClick) {
                    onItemMiddleClick(jeiItem)
                  }
                }}
              />
              {isFav && (
                <span
                  className="mc-inv-jei-favorite-star"
                  style={{
                    position: 'absolute',
                    top: 0,
                    right: 0,
                    lineHeight: 0,
                    pointerEvents: 'none',
                    color: '#ffd700',
                    filter: `drop-shadow(0 0 ${Math.max(1, scale)}px rgba(0,0,0,0.85))`,
                  }}
                >
                  <StarIcon size={Math.max(1, Math.round(6 * scale))} />
                </span>
              )}
            </div>
          )
        })}

      </div>
    </div>
  )
}
