import { useEffect, useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { motion, AnimatePresence } from 'framer-motion'
import { ArrowDownAZ, Calendar, Shuffle, Film, Tv, Filter, X, Star, Library } from '../lib/icons'
import { useLibraries, useLibraryItems } from '../hooks/use-jellyfin'
import PosterCard from '../components/ui/PosterCard'
import Select, { type SelectOption } from '../components/ui/Select'
import { resolutionLabel, videoRangeLabel } from '../lib/jellyfin-meta'
import { iconForGenre } from '../lib/genre-icons'
import { usePosterGridClasses } from '../lib/density'
import { type SavedSearchFilters } from '../stores/saved-searches-store'
import { useLibrarySelection } from '../stores/library-selection-store'
import BatchActionsBar from '../components/ui/BatchActionsBar'
import { WatchedPills, SavedSearchMenu } from './library/controls'
import { SaveSearchModal, SurpriseMeModal } from './library/modals'
import { EmptyLibrary, PosterGridSkeleton } from './library/states'

interface Props {
  type: 'movies' | 'shows'
}

const SORT_OPTIONS = [
  { value: 'SortName', label: 'A - Z', icon: ArrowDownAZ },
  { value: 'DateCreated', label: 'Recently added', icon: Calendar },
  { value: 'PremiereDate', label: 'Release date', icon: Calendar },
  { value: 'CommunityRating', label: 'Rating', icon: Star },
  { value: 'Random', label: 'Random', icon: Shuffle },
]

const COLLECTION_TYPE_MAP: Record<string, string> = {
  movies: 'movies',
  shows: 'tvshows',
}

const ITEM_TYPE_MAP: Record<string, string[]> = {
  movies: ['Movie'],
  shows: ['Series'],
}

const DECADE_OPTIONS: Array<{ value: string; label: string }> = [
  { value: '__any__', label: 'Any decade' },
  { value: '2020', label: '2020s' },
  { value: '2010', label: '2010s' },
  { value: '2000', label: '2000s' },
  { value: '1990', label: '1990s' },
  { value: '1980', label: '1980s' },
  { value: '1970', label: '1970s' },
  { value: '1960', label: '1960s' },
  { value: '1950', label: '1950s' },
  { value: '1900', label: 'Pre-1950' },
]

export default function LibraryPage({ type }: Props) {
  const [sortBy, setSortBy] = useState('SortName')
  const [genreFilter, setGenreFilter] = useState<string | null>(null)
  const [decade, setDecade] = useState<number | null>(null)
  const [watchedFilter, setWatchedFilter] = useState<'any' | 'played' | 'unplayed'>('any')
  const [only4K, setOnly4K] = useState(false)
  const [onlyHdr, setOnlyHdr] = useState(false)
  const [surpriseOpen, setSurpriseOpen] = useState(false)
  const [saveOpen, setSaveOpen] = useState(false)
  const [libraryFilter, setLibraryFilter] = useState<string>('') // '' = all
  const [layoutMode, setLayoutMode] = useState<'grid' | 'map'>(() => {
    if (typeof window === 'undefined') return 'grid'
    return (localStorage.getItem(`lib_layout:${type}`) as 'grid' | 'map') || 'grid'
  })
  const [mapShowAll, setMapShowAll] = useState(false)
  const navigate = useNavigate()
  const gridCls = usePosterGridClasses()
  const mapGridCls = 'grid grid-cols-6 sm:grid-cols-8 md:grid-cols-10 lg:grid-cols-12 xl:grid-cols-14 2xl:grid-cols-16 gap-2'
  const selected = useLibrarySelection(s => s.selected)
  const toggleSelect = useLibrarySelection(s => s.toggle)
  const clearSelection = useLibrarySelection(s => s.clear)

  function changeLayoutMode(next: 'grid' | 'map') {
    setLayoutMode(next)
    try { localStorage.setItem(`lib_layout:${type}`, next) } catch { /* noop */ }
  }

  // Clear any in-progress selection when navigating away from the page
  // or pressing Escape. Esc is the same dismiss affordance the playlist
  // view uses, so the muscle memory carries.
  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      if (e.key === 'Escape' && selected.size > 0) {
        clearSelection()
      }
    }
    window.addEventListener('keydown', onKey)
    return () => {
      window.removeEventListener('keydown', onKey)
    }
  }, [selected.size, clearSelection])
  useEffect(() => {
    return () => clearSelection()
  }, [clearSelection])

  const { data: libraries } = useLibraries()
  const collectionType = COLLECTION_TYPE_MAP[type]
  const matchingLibraries = useMemo(
    () => (libraries || []).filter(l => l.CollectionType === collectionType),
    [libraries, collectionType],
  )
  const parentId = libraryFilter || undefined
  const includeItemTypes = ITEM_TYPE_MAP[type]
  // If the previously selected library disappeared (e.g., the user
  // removed it on the server), fall back to "all libraries".
  useEffect(() => {
    if (libraryFilter && !matchingLibraries.some(l => l.Id === libraryFilter)) {
      setLibraryFilter('')
    }
  }, [libraryFilter, matchingLibraries])

  const filtersForApi = useMemo(() => {
    const f: string[] = []
    if (watchedFilter === 'played') f.push('IsPlayed')
    else if (watchedFilter === 'unplayed') f.push('IsUnplayed')
    return f.length > 0 ? f : undefined
  }, [watchedFilter])

  const yearsForApi = useMemo(() => {
    if (decade == null) return undefined
    if (decade === 1900) {
      const arr: number[] = []
      for (let y = 1900; y < 1950; y++) arr.push(y)
      return arr
    }
    return Array.from({ length: 10 }, (_, i) => decade + i)
  }, [decade])

  const sortOrder = useMemo(() => {
    if (sortBy === 'Random') return ['Descending']
    if (sortBy === 'CommunityRating' || sortBy === 'DateCreated' || sortBy === 'PremiereDate') {
      return ['Descending']
    }
    return ['Ascending']
  }, [sortBy])

  const { data, isLoading } = useLibraryItems(parentId, {
    sortBy: [sortBy],
    sortOrder,
    includeItemTypes,
    filters: filtersForApi,
    years: yearsForApi,
    limit: 500,
  })

  const allItems = useMemo(() => data?.Items || [], [data?.Items])

  const allGenres = useMemo(() => {
    const set = new Set<string>()
    for (const it of allItems) {
      for (const g of it.Genres || []) set.add(g)
    }
    return [...set].sort((a, b) => a.localeCompare(b))
  }, [allItems])

  const items = useMemo(() => {
    return allItems.filter(it => {
      if (genreFilter && !(it.Genres || []).includes(genreFilter)) return false
      if (only4K && resolutionLabel(it) !== '4K') return false
      if (onlyHdr && !videoRangeLabel(it)) return false
      return true
    })
  }, [allItems, genreFilter, only4K, onlyHdr])

  const totalCount = items.length
  const filtered = !!(genreFilter || only4K || onlyHdr || decade != null || watchedFilter !== 'any')
  const Icon = type === 'movies' ? Film : Tv

  const currentFilters: SavedSearchFilters = {
    sortBy,
    watched: watchedFilter,
    genre: genreFilter,
    decade,
    only4K,
    onlyHdr,
  }

  function applySaved(filters: SavedSearchFilters) {
    setSortBy(filters.sortBy)
    setWatchedFilter(filters.watched)
    setGenreFilter(filters.genre)
    setDecade(filters.decade)
    setOnly4K(filters.only4K)
    setOnlyHdr(filters.onlyHdr)
  }

  function clearAll() {
    setGenreFilter(null)
    setDecade(null)
    setWatchedFilter('any')
    setOnly4K(false)
    setOnlyHdr(false)
  }

  return (
    <div className="px-7 pt-4 pb-12">
      {/* Header */}
      <div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4 mb-7">
        <div>
          <div className="flex items-center gap-2 mb-1.5">
            <span className="w-1 h-3.5 rounded-full bg-accent" />
            <span className="text-[11px] font-semibold text-text-2 uppercase tracking-[0.14em]">Library</span>
          </div>
          <div className="flex items-baseline gap-3">
            <h1 className="text-3xl md:text-4xl font-bold font-display text-text-1 tracking-tight">
              {type === 'movies' ? 'Movies' : 'TV shows'}
            </h1>
            {totalCount > 0 && (
              <span className="text-[12px] text-text-4 tabular-nums">
                {totalCount.toLocaleString()} {totalCount === 1 ? 'item' : 'items'}
              </span>
            )}
          </div>
        </div>

        {/* Sort chips */}
        <div className="flex items-center gap-1.5 p-1 bg-elevated/50 border border-border rounded-lg w-fit">
          {SORT_OPTIONS.map(opt => {
            const isActive = sortBy === opt.value
            return (
              <button
                key={opt.value}
                onClick={() => setSortBy(opt.value)}
                className={`relative h-7 px-2.5 rounded-md text-[11.5px] font-medium tracking-tight transition-colors duration-150 flex items-center gap-1.5 focus-ring ${
                  isActive ? 'text-void' : 'text-text-3 hover:text-text-1'
                }`}
              >
                {isActive && (
                  <motion.span
                    layoutId="sort-active"
                    className="absolute inset-0 bg-accent rounded-md shadow-sm"
                    transition={{ type: 'spring', stiffness: 380, damping: 30 }}
                  />
                )}
                <opt.icon size={12} className="relative" strokeWidth={2.25} />
                <span className="relative hidden sm:inline">{opt.label}</span>
              </button>
            )
          })}
        </div>
      </div>

      {/* Filters */}
      <div className="mb-6 flex items-center gap-2 flex-wrap">
        {matchingLibraries.length > 1 && (
          <Select
            size="sm"
            ariaLabel="Filter by library"
            triggerIcon={<Library size={11} stroke={2} />}
            value={libraryFilter || '__all__'}
            onChange={v => setLibraryFilter(v === '__all__' ? '' : v)}
            width="min-w-[160px]"
            options={[
              {
                value: '__all__',
                label: `All libraries (${matchingLibraries.length})`,
                muted: true,
              } as SelectOption<string>,
              ...matchingLibraries.map<SelectOption<string>>(l => ({
                value: l.Id!,
                label: l.Name || 'Library',
              })),
            ]}
          />
        )}

        <WatchedPills value={watchedFilter} onChange={setWatchedFilter} />

        <button
          onClick={() => setOnly4K(o => !o)}
          className={`inline-flex items-center gap-1 h-7 px-2.5 rounded-md text-[11px] font-bold uppercase tracking-wider transition-all duration-150 focus-ring ${
            only4K
              ? 'bg-cool/25 text-cool border border-cool/40'
              : 'bg-elevated/50 text-text-3 border border-border hover:text-text-1 hover:border-border-hover'
          }`}
        >
          4K
        </button>
        <button
          onClick={() => setOnlyHdr(o => !o)}
          className={`inline-flex items-center gap-1 h-7 px-2.5 rounded-md text-[11px] font-bold uppercase tracking-wider transition-all duration-150 focus-ring ${
            onlyHdr
              ? 'bg-amber-500/25 text-amber-200 border border-amber-400/40'
              : 'bg-elevated/50 text-text-3 border border-border hover:text-text-1 hover:border-border-hover'
          }`}
        >
          HDR/DV
        </button>

        <Select
          size="sm"
          ariaLabel="Filter by decade"
          triggerIcon={<Calendar size={11} stroke={2} />}
          value={decade == null ? '__any__' : String(decade)}
          onChange={v => setDecade(v === '__any__' ? null : Number(v))}
          width="min-w-[120px]"
          options={DECADE_OPTIONS as SelectOption<string>[]}
        />

        {allGenres.length > 0 && (
          <Select
            size="sm"
            ariaLabel="Filter by genre"
            triggerIcon={<Filter size={11} stroke={2} />}
            value={genreFilter ?? '__all__'}
            onChange={v => setGenreFilter(v === '__all__' ? null : v)}
            width="min-w-[160px]"
            options={[
              { value: '__all__', label: 'All genres', muted: true } as SelectOption<string>,
              ...allGenres.map<SelectOption<string>>(g => ({
                value: g,
                label: g,
                icon: iconForGenre(g),
              })),
            ]}
          />
        )}

        {filtered && (
          <button
            onClick={clearAll}
            className="inline-flex items-center gap-1 h-7 px-2 rounded-md text-[11px] text-text-3 hover:text-text-1 transition-colors focus-ring"
          >
            <X size={11} />
            Clear
          </button>
        )}

        <SavedSearchMenu
          scope={type}
          onApply={applySaved}
          onSave={() => setSaveOpen(true)}
        />

        <button
          onClick={() => setSurpriseOpen(true)}
          disabled={items.length === 0}
          className="inline-flex items-center gap-1.5 h-7 px-3 rounded-md text-[11.5px] font-medium tracking-tight bg-accent/10 text-accent ring-1 ring-accent/30 hover:bg-accent/15 transition disabled:opacity-40 focus-ring"
        >
          <Shuffle size={11} stroke={2.25} />
          Surprise me
        </button>

        {filtered && (
          <span className="ml-auto inline-flex items-center gap-1.5 text-[11px] text-text-4">
            <Filter size={10} className="text-accent" />
            <span className="tabular-nums">{totalCount}</span> match{totalCount === 1 ? '' : 'es'}
          </span>
        )}
      </div>

      {/* Layout toggle - sits next to the matches counter */}
      <div className="flex items-center justify-end gap-1 mb-3 -mt-3">
        <span className="text-[10.5px] uppercase tracking-[0.16em] font-semibold text-text-4 mr-1">
          View
        </span>
        <div className="inline-flex items-center bg-elevated/40 ring-1 ring-border rounded-md p-0.5">
          <button
            onClick={() => changeLayoutMode('grid')}
            className={`h-6 px-2.5 rounded text-[11px] font-medium tracking-tight transition focus-ring ${
              layoutMode === 'grid' ? 'bg-accent text-void' : 'text-text-3 hover:text-text-1'
            }`}
          >
            Grid
          </button>
          <button
            onClick={() => changeLayoutMode('map')}
            className={`h-6 px-2.5 rounded text-[11px] font-medium tracking-tight transition focus-ring ${
              layoutMode === 'map' ? 'bg-accent text-void' : 'text-text-3 hover:text-text-1'
            }`}
          >
            Map
          </button>
        </div>
      </div>

      {/* Grid */}
      {isLoading ? (
        <PosterGridSkeleton />
      ) : items.length === 0 ? (
        <EmptyLibrary type={type} Icon={Icon} />
      ) : (
        <AnimatePresence mode="popLayout">
          <motion.div
            key={`${sortBy}-${layoutMode}`}
            initial={{ opacity: 0, y: 8 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -4 }}
            transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
            className={layoutMode === 'map' ? mapGridCls : gridCls}
            onClick={(e) => {
              // Click on the grid background (not on a card) clears the selection.
              if (e.target === e.currentTarget && selected.size > 0) clearSelection()
            }}
          >
            {(layoutMode === 'map' && !mapShowAll ? items.slice(0, 240) : items).map((item, i) => (
              <motion.div
                key={item.Id}
                initial={{ opacity: 0, y: 8 }}
                animate={{ opacity: 1, y: 0 }}
                transition={{ duration: 0.3, delay: Math.min(i * 0.012, 0.3), ease: [0.16, 1, 0.3, 1] }}
              >
                <PosterCard
                  item={item}
                  priority={i < 12}
                  selected={item.Id ? selected.has(item.Id) : false}
                  onToggleSelect={item.Id ? () => toggleSelect(item.Id!) : undefined}
                  libraryContext
                  compact={layoutMode === 'map'}
                  onClick={() => {
                    if (selected.size > 0) {
                      // While in selection mode, plain clicks toggle membership too.
                      if (item.Id) toggleSelect(item.Id)
                      return
                    }
                    if (item.Id) navigate(`/item/${item.Id}`)
                  }}
                />
              </motion.div>
            ))}
          </motion.div>
        </AnimatePresence>
      )}

      {layoutMode === 'map' && !mapShowAll && items.length > 240 && (
        <div className="mt-6 text-center">
          <button
            onClick={() => setMapShowAll(true)}
            className="inline-flex items-center gap-2 h-9 px-4 rounded-full bg-elevated/60 ring-1 ring-border hover:ring-accent/40 hover:text-accent text-[12px] text-text-2 font-medium tracking-tight transition focus-ring"
          >
            Load all {items.length} items
          </button>
        </div>
      )}

      <BatchActionsBar />

      <SurpriseMeModal
        open={surpriseOpen}
        items={items}
        onClose={() => setSurpriseOpen(false)}
      />
      <SaveSearchModal
        open={saveOpen}
        scope={type}
        filters={currentFilters}
        onClose={() => setSaveOpen(false)}
      />
    </div>
  )
}

