'use client'

import { useEffect, useRef, useState, useCallback } from 'react'
import type { TvSeries, TvSeason, TvEpisode } from '@/types'

import FilterPanel from '@/components/FilterPanel'
import VideoPlayerModal from '@/components/mixed/VideoPlayerModal'
import MediaTagPanel from '@/components/tags/MediaTagPanel'
import TagSelector from '@/components/tags/TagSelector'
import AssignedTagBadges from '@/components/tags/AssignedTagBadges'
import EpisodeCard from './EpisodeCard'
import DoomScrollView, { type DoomScrollItem } from '@/components/DoomScrollView'
import { isBrowserPlayable } from '@/lib/browser-media'

interface Props {
  libraryId: string
  readOnly?: boolean
}

type ViewLevel = 'series' | 'seasons' | 'episodes'

export default function TvView({ libraryId, readOnly }: Props) {
  const [view, setView] = useState<ViewLevel>('series')
  const [series, setSeries] = useState<TvSeries[]>([])
  const [seasons, setSeasons] = useState<TvSeason[]>([])
  const [episodes, setEpisodes] = useState<TvEpisode[]>([])
  const [selectedSeries, setSelectedSeries] = useState<TvSeries | null>(null)
  const [selectedSeason, setSelectedSeason] = useState<TvSeason | null>(null)
  const [playingEpisodeIndex, setPlayingEpisodeIndex] = useState<number | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [search, setSearch] = useState('')
  const [selectedTagIds, setSelectedTagIds] = useState<Set<string>>(new Set())
  const [assignments, setAssignments] = useState<Record<string, string[]>>({})
  const [seriesEpisodeTags, setSeriesEpisodeTags] = useState<Record<string, string[]>>({})
  const [filterRefreshKey, setFilterRefreshKey] = useState(0)
  const [showFilters, setShowFilters] = useState(
    () => typeof window !== 'undefined' && window.innerWidth >= 768
  )
  const [selectedSeriesIndex, setSelectedSeriesIndex] = useState<number | null>(null)
  const [selectedSeasonIndex, setSelectedSeasonIndex] = useState<number | null>(null)
  const [tagPanel, setTagPanel] = useState<{ itemKey: string; title: string } | null>(null)
  const [menuOpen, setMenuOpen] = useState(false)
  const [confirming, setConfirming] = useState(false)
  const [deleting, setDeleting] = useState(false)
  const [refreshingMeta, setRefreshingMeta] = useState(false)
  const [editingMeta, setEditingMeta] = useState(false)
  const [savingMeta, setSavingMeta] = useState(false)
  const [editForm, setEditForm] = useState({ title: '', year: '', plot: '', genres: '' })
  const [warnRefresh, setWarnRefresh] = useState(false)
  const [renaming, setRenaming] = useState(false)
  const [renameName, setRenameName] = useState('')
  const [renameError, setRenameError] = useState<string | null>(null)
  const [renameSaving, setRenameSaving] = useState(false)
  const [doomScrollActive, setDoomScrollActive] = useState(false)
  const [doomScrollItems, setDoomScrollItems] = useState<DoomScrollItem[]>([])
  const [doomScrollLoading, setDoomScrollLoading] = useState(false)
  const [showTagPanel, setShowTagPanel] = useState(false)
  const [tagPanelItemKey, setTagPanelItemKey] = useState<string | null>(null)
  const [tagPanelDisabled, setTagPanelDisabled] = useState(false)
  const [tagRefreshKey, setTagRefreshKey] = useState(0)
  const menuRef = useRef<HTMLDivElement>(null)
  const smallBtn = 'w-7 h-7 rounded-full flex items-center justify-center transition-colors flex-shrink-0'

  const toggleTag = (tagId: string) =>
    setSelectedTagIds((prev) => {
      const next = new Set(prev)
      next.has(tagId) ? next.delete(tagId) : next.add(tagId)
      return next
    })

  const fetchSeries = useCallback(() => {
    setLoading(true)
    setError(null)
    fetch(`/api/tv?libraryId=${encodeURIComponent(libraryId)}`)
      .then((r) => r.json())
      .then((data) => { setSeries(data); setLoading(false) })
      .catch(() => { setError('Failed to load TV library'); setLoading(false) })
  }, [libraryId])

  useEffect(() => { fetchSeries() }, [fetchSeries])

  const fetchAssignments = useCallback(() => {
    fetch(`/api/tags/library-assignments?libraryId=${encodeURIComponent(libraryId)}`)
      .then((r) => r.json())
      .then(setAssignments)
      .catch(() => {})
  }, [libraryId])

  useEffect(() => { fetchAssignments() }, [fetchAssignments])

  const fetchSeriesEpisodeTags = useCallback(() => {
    fetch(`/api/tv/series-episode-tags?libraryId=${encodeURIComponent(libraryId)}`)
      .then((r) => r.json())
      .then(setSeriesEpisodeTags)
      .catch(() => {})
  }, [libraryId])

  useEffect(() => { fetchSeriesEpisodeTags() }, [fetchSeriesEpisodeTags])

  const openSeries = (s: TvSeries) => {
    setSelectedSeriesIndex(filteredSeries.indexOf(s))
    setSelectedSeries(s)
    setView('seasons')
    setLoading(true)
    setError(null)
    fetch(`/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(s.id)}`)
      .then((r) => r.json())
      .then((data: TvSeason[]) => {
        setSeasons(data)
        setLoading(false)
      })
      .catch(() => { setError('Failed to load seasons'); setLoading(false) })
  }

  const openSeason = (season: TvSeason, index?: number) => {
    setSelectedSeasonIndex(index ?? seasons.indexOf(season))
    setSelectedSeason(season)
    setView('episodes')
    if (showTagPanel) {
      setTagPanelDisabled(true)
    }
    setLoading(true)
    setError(null)
    fetch(
      `/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(season.seriesId)}&seasonId=${encodeURIComponent(season.id)}`
    )
      .then((r) => r.json())
      .then((data) => { setEpisodes(data); setLoading(false) })
      .catch(() => { setError('Failed to load episodes'); setLoading(false) })
  }

  // Close menu on outside click
  useEffect(() => {
    if (!menuOpen) return
    const handler = (e: MouseEvent) => {
      if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
        setMenuOpen(false)
      }
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [menuOpen])

  const goToSeries = () => {
    setView('series')
    setSelectedSeries(null)
    setSelectedSeason(null)
    setSelectedSeriesIndex(null)
    setSelectedSeasonIndex(null)
    setMenuOpen(false)
    setConfirming(false)
    setShowTagPanel(false)
    setTagPanelItemKey(null)
    setTagPanelDisabled(false)
  }

  const goToSeasons = () => {
    setView('seasons')
    setSelectedSeason(null)
    setSelectedSeasonIndex(null)
    setConfirming(false)
    if (showTagPanel && selectedSeries?.item_key) {
      setTagPanelItemKey(selectedSeries.item_key)
      setTagPanelDisabled(false)
    }
  }

  const handleDeleteSeries = () => {
    if (!selectedSeries) return
    setDeleting(true)
    fetch(
      `/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(selectedSeries.id)}`,
      { method: 'DELETE' }
    )
      .then(() => {
        setSeries((prev) => prev.filter((s) => s.id !== selectedSeries.id))
        goToSeries()
        setDeleting(false)
      })
      .catch(() => setDeleting(false))
  }

  const doRefreshSeriesMetadata = () => {
    if (!selectedSeries) return
    setRefreshingMeta(true)
    setWarnRefresh(false)
    const itemKey = `${libraryId}:tv_series:${selectedSeries.id}`
    const currentId = selectedSeries.id
    fetch(
      `/api/nfo-refresh?libraryId=${encodeURIComponent(libraryId)}&itemType=tv_series&itemKey=${encodeURIComponent(itemKey)}&includeEpisodes=true`,
      { method: 'POST' }
    )
      .then(() => fetch(`/api/tv?libraryId=${encodeURIComponent(libraryId)}`))
      .then((r) => r.json())
      .then((data: TvSeries[]) => {
        setSeries(data)
        const updated = data.find((s) => s.id === currentId)
        if (updated) setSelectedSeries(updated)
      })
      .finally(() => setRefreshingMeta(false))
  }

  const handleRefreshSeriesMetadata = () => {
    setMenuOpen(false)
    if (selectedSeries?.manuallyEdited) {
      setWarnRefresh(true)
    } else {
      doRefreshSeriesMetadata()
    }
  }

  const handleStartEditingMeta = () => {
    if (!selectedSeries) return
    setMenuOpen(false)
    setEditForm({
      title: selectedSeries.title,
      year: selectedSeries.year?.toString() ?? '',
      plot: selectedSeries.plot ?? '',
      genres: selectedSeries.genres.join(', '),
    })
    setEditingMeta(true)
  }

  const handleSaveSeriesMetadata = () => {
    if (!selectedSeries) return
    setSavingMeta(true)
    const genres = editForm.genres.split(',').map((g) => g.trim()).filter(Boolean)
    const yearNum = editForm.year ? parseInt(editForm.year, 10) : null
    fetch('/api/metadata', {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        itemKey: selectedSeries.item_key,
        title: editForm.title,
        year: isNaN(yearNum as number) ? null : yearNum,
        plot: editForm.plot || null,
        genres,
      }),
    })
      .then(() => { setEditingMeta(false); fetchSeries() })
      .finally(() => setSavingMeta(false))
  }

  const handleStartRename = () => {
    if (!selectedSeries) return
    setMenuOpen(false)
    setRenameName(decodeURIComponent(selectedSeries.id))
    setRenameError(null)
    setRenaming(true)
  }

  const handleRename = () => {
    if (!selectedSeries) return
    const trimmed = renameName.trim()
    if (!trimmed) return
    setRenameSaving(true)
    setRenameError(null)
    fetch('/api/rename', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        libraryId,
        oldPath: decodeURIComponent(selectedSeries.id),
        newName: trimmed,
        itemType: 'tv_series',
      }),
    })
      .then(async (res) => {
        if (res.status === 409) {
          const data = await res.json()
          setRenameError(data.error)
          return
        }
        if (!res.ok) throw new Error()
        setRenaming(false)
        fetchSeries()
      })
      .catch(() => setRenameError('Rename failed'))
      .finally(() => setRenameSaving(false))
  }

  const handleDoomScroll = async () => {
    setDoomScrollLoading(true)
    try {
      let items: DoomScrollItem[]
      if (filtersActive && filteredSeries.length < series.length) {
        // Fetch episodes only from the filtered series
        const episodeLists = await Promise.all(
          filteredSeries.map(async (s) => {
            const seasons: TvSeason[] = await fetch(
              `/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(s.id)}`
            ).then((r) => r.json())
            const seasonEps = await Promise.all(
              seasons.map((season) =>
                fetch(
                  `/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(s.id)}&seasonId=${encodeURIComponent(season.id)}`
                ).then((r) => r.json() as Promise<TvEpisode[]>)
              )
            )
            return seasonEps.flat()
          })
        )
        items = episodeLists.flat().filter((ep) => isBrowserPlayable(ep.videoPath)).map((ep) => ({
          url: `/api/file?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(ep.videoPath)}`,
          name: ep.title,
          mediaType: 'video' as const,
        }))
      } else {
        // No filters — fetch all episodes via the TV API hierarchy
        const allSeries: TvSeries[] = await fetch(
          `/api/tv?libraryId=${encodeURIComponent(libraryId)}`
        ).then((r) => r.json())
        const episodeLists = await Promise.all(
          allSeries.map(async (s) => {
            const seasons: TvSeason[] = await fetch(
              `/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(s.id)}`
            ).then((r) => r.json())
            const seasonEps = await Promise.all(
              seasons.map((season) =>
                fetch(
                  `/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(s.id)}&seasonId=${encodeURIComponent(season.id)}`
                ).then((r) => r.json() as Promise<TvEpisode[]>)
              )
            )
            return seasonEps.flat()
          })
        )
        items = episodeLists.flat().filter((ep) => isBrowserPlayable(ep.videoPath)).map((ep) => ({
          url: `/api/file?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(ep.videoPath)}`,
          name: ep.title,
          mediaType: 'video' as const,
        }))
      }
      setDoomScrollItems(items)
      setDoomScrollActive(true)
    } catch {
      // ignore
    } finally {
      setDoomScrollLoading(false)
    }
  }

  // Escape key + body scroll lock when modal is open
  useEffect(() => {
    if (view === 'series') return
    const handleKey = (e: KeyboardEvent) => {
      if (e.key !== 'Escape') return
      if (menuOpen) { setMenuOpen(false); return }
      if (showTagPanel) { setShowTagPanel(false); return }
      if (view === 'episodes') {
        setView('seasons')
        setSelectedSeason(null)
        setConfirming(false)
        if (selectedSeries?.item_key) {
          setTagPanelItemKey(selectedSeries.item_key)
          setTagPanelDisabled(false)
        }
        return
      }
      setView('series')
      setSelectedSeries(null)
      setSelectedSeason(null)
      setMenuOpen(false)
      setConfirming(false)
      setShowTagPanel(false)
      setTagPanelItemKey(null)
      setTagPanelDisabled(false)
    }
    document.addEventListener('keydown', handleKey)
    document.body.style.overflow = 'hidden'
    return () => {
      document.removeEventListener('keydown', handleKey)
      document.body.style.overflow = ''
    }
  }, [view, menuOpen, showTagPanel, selectedSeries])

  const filtersActive = search !== '' || selectedTagIds.size > 0

  const filteredSeries = series.filter((s) => {
    if (search && !s.title.toLowerCase().includes(search.toLowerCase())) return false
    if (selectedTagIds.size > 0) {
      const seriesTags = assignments[s.item_key!] ?? []
      const episodeTags = seriesEpisodeTags[s.id] ?? []
      const allTags = seriesTags.length === 0 ? episodeTags
        : episodeTags.length === 0 ? seriesTags
        : [...new Set([...seriesTags, ...episodeTags])]
      if (![...selectedTagIds].every((id) => allTags.includes(id))) return false
    }
    return true
  })

  const filteredEpisodes = episodes.filter((ep) => {
    if (search && !ep.title.toLowerCase().includes(search.toLowerCase())) return false
    if (selectedTagIds.size > 0) {
      const epTags = assignments[ep.item_key!] ?? []
      if (![...selectedTagIds].every((id) => epTags.includes(id))) return false
    }
    return true
  })

  // Arrow key navigation for series/season levels (mirrors the prev/next UI buttons)
  useEffect(() => {
    if (view === 'series') return
    const handleArrowKey = (e: KeyboardEvent) => {
      if (e.key === 'ArrowLeft') {
        if (view === 'seasons' && selectedSeriesIndex !== null && selectedSeriesIndex > 0)
          openSeries(filteredSeries[selectedSeriesIndex - 1])
        else if (view === 'episodes' && selectedSeasonIndex !== null && selectedSeasonIndex > 0)
          openSeason(seasons[selectedSeasonIndex - 1], selectedSeasonIndex - 1)
      }
      if (e.key === 'ArrowRight') {
        if (view === 'seasons' && selectedSeriesIndex !== null && selectedSeriesIndex < filteredSeries.length - 1)
          openSeries(filteredSeries[selectedSeriesIndex + 1])
        else if (view === 'episodes' && selectedSeasonIndex !== null && selectedSeasonIndex < seasons.length - 1)
          openSeason(seasons[selectedSeasonIndex + 1], selectedSeasonIndex + 1)
      }
    }
    document.addEventListener('keydown', handleArrowKey)
    return () => document.removeEventListener('keydown', handleArrowKey)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [view, selectedSeriesIndex, selectedSeasonIndex, filteredSeries, seasons])

  const playingEpisode = playingEpisodeIndex !== null ? episodes[playingEpisodeIndex] ?? null : null

  if (playingEpisode && playingEpisodeIndex !== null) {
    const videoUrl = `/api/file?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(playingEpisode.videoPath)}`
    return (
      <VideoPlayerModal
        url={videoUrl}
        name={playingEpisode.title}
        itemKey={playingEpisode.item_key!}
        onTagsChanged={() => { setFilterRefreshKey((k) => k + 1); fetchAssignments(); fetchSeriesEpisodeTags() }}
        onClose={() => setPlayingEpisodeIndex(null)}
        onPrev={playingEpisodeIndex > 0 ? () => setPlayingEpisodeIndex((i) => (i !== null ? i - 1 : null)) : undefined}
        onNext={playingEpisodeIndex < episodes.length - 1 ? () => setPlayingEpisodeIndex((i) => (i !== null ? i + 1 : null)) : undefined}
        context="tv"
        readOnly={readOnly}
      />
    )
  }

  return (
    <div>
      {doomScrollActive && doomScrollItems.length > 0 && (
        <DoomScrollView
          items={doomScrollItems}
          videoContext="tv"
          onClose={() => setDoomScrollActive(false)}
        />
      )}

      {/* Breadcrumb */}
      <div className="flex items-center gap-2 mb-6 text-sm flex-wrap">
        {view !== 'series' ? (
          <button onClick={goToSeries} className="transition-colors" style={{ color: 'var(--accent)' }}>
            All Series
          </button>
        ) : (
          <span style={{ color: 'var(--text-secondary)' }}>All Series</span>
        )}
        {selectedSeries && (
          <>
            <span style={{ color: 'var(--text-secondary)' }}>/</span>
            {view === 'episodes' ? (
              <button onClick={goToSeasons} className="transition-colors" style={{ color: 'var(--accent)' }}>
                {selectedSeries.title}
              </button>
            ) : (
              <span className="font-medium" style={{ color: 'var(--text-primary)' }}>
                {selectedSeries.title}
              </span>
            )}
          </>
        )}
        {selectedSeason && (
          <>
            <span style={{ color: 'var(--text-secondary)' }}>/</span>
            <span className="font-medium" style={{ color: 'var(--text-primary)' }}>
              {selectedSeason.title}
            </span>
          </>
        )}
      </div>

      {view === 'series' && (
        <>
          <div className="flex items-center gap-2 mb-4">
            <button
              onClick={() => setShowFilters((v) => !v)}
              className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-colors"
              style={{
                backgroundColor: (showFilters || filtersActive) ? 'var(--accent)' : 'var(--surface)',
                color: (showFilters || filtersActive) ? '#fff' : 'var(--text-secondary)',
                border: '1px solid var(--border)',
              }}
              aria-label={showFilters ? 'Hide filters' : 'Show filters'}
            >
              Filters{filtersActive ? ' ●' : ''}
            </button>
            <button
              onClick={handleDoomScroll}
              disabled={doomScrollLoading}
              className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-colors disabled:opacity-50"
              style={{
                backgroundColor: 'var(--surface)',
                color: 'var(--text-secondary)',
                border: '1px solid var(--border)',
              }}
              onMouseEnter={(e) => { if (!doomScrollLoading) (e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface-hover)' }}
              onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface)' }}
            >
              {doomScrollLoading ? 'Loading…' : 'Doom Scroll'}
            </button>
          </div>
          <div className="flex flex-col md:flex-row gap-6 md:items-start">
            {showFilters && (
              <div className="w-full md:w-52 md:flex-shrink-0">
                <FilterPanel
                  libraryId={libraryId}
                  assignments={assignments}
                  search={search}
                  onSearchChange={setSearch}
                  selectedTagIds={selectedTagIds}
                  onTagToggle={toggleTag}
                  refreshKey={filterRefreshKey}
                />
              </div>
            )}
            <div className="flex-1 min-w-0">
            {loading ? (
              <SeriesLoadingGrid />
            ) : error ? (
              <ErrorMsg message={error} />
            ) : series.length === 0 ? (
              <div className="rounded-lg border p-12 text-center" style={{ borderColor: 'var(--border)', color: 'var(--text-secondary)' }}>
                <p className="text-lg mb-1">No TV shows found</p>
                <p className="text-sm">Each series should be a folder containing season subdirectories with video files.</p>
              </div>
            ) : (
              <div className="grid gap-4 grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
                {filteredSeries.map((s) => (
                  <div
                    key={s.id}
                    role="button"
                    tabIndex={0}
                    onClick={() => openSeries(s)}
                    onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openSeries(s) } }}
                    className="group text-left rounded-xl overflow-hidden border transition-all focus:outline-none focus-visible:ring-2 cursor-pointer"
                    style={{ borderColor: 'var(--border)', backgroundColor: 'var(--surface)' }}
                    onMouseEnter={(e) => {
                      ;(e.currentTarget as HTMLElement).style.borderColor = 'var(--accent)'
                      ;(e.currentTarget as HTMLElement).style.transform = 'translateY(-2px)'
                    }}
                    onMouseLeave={(e) => {
                      ;(e.currentTarget as HTMLElement).style.borderColor = 'var(--border)'
                      ;(e.currentTarget as HTMLElement).style.transform = 'translateY(0)'
                    }}
                  >
                    <div className="aspect-[2/3] w-full relative overflow-hidden" style={{ backgroundColor: 'var(--border)' }}>
                      {s.posterUrl ? (
                        // eslint-disable-next-line @next/next/no-img-element
                        <img src={s.posterUrl} alt={s.title} className="absolute inset-0 w-full h-full object-cover" />
                      ) : (
                        <div className="absolute inset-0 flex items-center justify-center text-4xl">📺</div>
                      )}
                      <button
                        onClick={(e) => { e.stopPropagation(); setTagPanel({ itemKey: s.item_key!, title: s.title }) }}
                        className="absolute top-2 left-2 w-6 h-6 rounded-full items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-opacity hidden group-hover:flex"
                        style={{ backgroundColor: 'rgba(0,0,0,0.55)', color: '#fff' }}
                        aria-label={`Tag ${s.title}`}
                        title="Tags"
                      >
                        🏷
                      </button>
                    </div>
                    <div className="p-2">
                      <p className="text-xs font-medium truncate leading-tight" style={{ color: 'var(--text-primary)' }} title={s.title}>
                        {s.title}
                      </p>
                      <p className="text-xs mt-0.5" style={{ color: 'var(--text-secondary)' }}>
                        {s.year ? `${s.year} · ` : ''}{s.seasonCount} season{s.seasonCount !== 1 ? 's' : ''}
                      </p>
                    </div>
                  </div>
                ))}
              </div>
            )}
            </div>
          </div>
          {tagPanel && (
            <div
              className="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-4"
              style={{ backgroundColor: 'rgba(0,0,0,0.6)' }}
              onClick={(e) => { if (e.target === e.currentTarget) setTagPanel(null) }}
            >
              <div
                className="w-full max-w-md rounded-2xl shadow-2xl overflow-hidden"
                style={{ backgroundColor: 'var(--surface)', border: '1px solid var(--border)' }}
              >
                <div className="flex items-center justify-between px-5 py-4" style={{ borderBottom: '1px solid var(--border)' }}>
                  <div className="min-w-0">
                    <p className="text-xs font-semibold uppercase tracking-wider mb-0.5" style={{ color: 'var(--text-secondary)' }}>
                      Tags
                    </p>
                    <p className="text-sm font-medium truncate" style={{ color: 'var(--text-primary)' }}>
                      {tagPanel.title}
                    </p>
                  </div>
                  <button
                    onClick={() => setTagPanel(null)}
                    className="ml-4 w-8 h-8 flex-shrink-0 rounded-full flex items-center justify-center text-sm transition-colors"
                    style={{ backgroundColor: 'var(--border)', color: 'var(--text-secondary)' }}
                    onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.color = 'var(--text-primary)')}
                    onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.color = 'var(--text-secondary)')}
                    aria-label="Close"
                  >
                    ✕
                  </button>
                </div>
                <div className="px-5 py-4">
                  <TagSelector
                    itemKey={tagPanel.itemKey}
                    onTagsChanged={() => { setFilterRefreshKey((k) => k + 1); fetchAssignments(); fetchSeriesEpisodeTags() }}
                  />
                </div>
              </div>
            </div>
          )}
        </>
      )}

      {(view === 'seasons' || view === 'episodes') && (
        <div
          className="fixed inset-0 z-50 overflow-hidden"
          style={{ backgroundColor: 'rgba(0,0,0,0.75)', height: '100vh' }}
        >
          <div className={`flex h-full w-full ${showTagPanel ? 'flex-col md:flex-row' : ''}`}>
            <div className="flex-1 min-h-0 min-w-0 relative" onClick={goToSeries}>
              <div className="h-full overflow-y-auto flex items-center justify-center p-4">
                <div
                  className="w-full max-w-3xl rounded-2xl overflow-hidden shadow-2xl"
                  style={{ backgroundColor: 'var(--surface)', border: '1px solid var(--border)' }}
                  onClick={(e) => e.stopPropagation()}
                >
                  {view === 'episodes' && (
                    <div className="flex items-center gap-2 px-5 py-3 flex-shrink-0" style={{ borderBottom: '1px solid var(--border)' }}>
                      <button
                        onClick={(e) => { e.stopPropagation(); goToSeasons() }}
                        className="text-sm transition-colors hover:underline"
                        style={{ color: 'var(--accent)' }}
                      >
                        ‹ {selectedSeries?.title}
                      </button>
                      <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>·</span>
                      <span className="text-sm font-medium truncate" style={{ color: 'var(--text-primary)' }}>
                        {selectedSeason?.title}
                      </span>
                    </div>
                  )}
      {view === 'seasons' && selectedSeries && (
        <div>
          {/* Series info header */}
          <div className="mb-6 p-4 rounded-xl" style={{ backgroundColor: 'var(--surface)', border: '1px solid var(--border)' }}>
            <div className="flex items-start gap-4">
              {selectedSeries.posterUrl && (
                // eslint-disable-next-line @next/next/no-img-element
                <img src={selectedSeries.posterUrl} alt={selectedSeries.title} className="w-16 rounded-lg object-cover flex-shrink-0" style={{ aspectRatio: '2/3' }} />
              )}
              <div className="flex-1 min-w-0">
                <div className="flex items-start gap-2">
                  <h2 className="text-lg font-semibold flex-1 min-w-0" style={{ color: 'var(--text-primary)' }}>{selectedSeries.title}</h2>
                  {/* Kebab menu */}
                  <div className="relative flex-shrink-0" ref={menuRef}>
                    <button
                      onClick={() => { setMenuOpen((o) => !o); setConfirming(false) }}
                      className="w-7 h-7 rounded-lg flex items-center justify-center transition-colors"
                      style={{ color: 'var(--text-secondary)', backgroundColor: 'transparent' }}
                      onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
                      onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
                      aria-label="More options"
                    >
                      ⋮
                    </button>
                    {menuOpen && (
                      <div
                        className="absolute right-0 top-full mt-1 rounded-lg shadow-lg overflow-hidden z-20 min-w-max"
                        style={{ backgroundColor: 'var(--surface)', border: '1px solid var(--border)' }}
                      >
                        <button
                          onClick={handleRefreshSeriesMetadata}
                          disabled={refreshingMeta}
                          className="flex items-center gap-2 w-full px-4 py-2 text-sm text-left transition-colors disabled:opacity-50"
                          style={{ color: 'var(--text-primary)' }}
                          onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
                          onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
                        >
                          {refreshingMeta ? 'Refreshing…' : 'Refresh metadata'}
                        </button>
                        <button
                          onClick={handleStartEditingMeta}
                          className="flex items-center gap-2 w-full px-4 py-2 text-sm text-left transition-colors"
                          style={{ color: 'var(--text-primary)' }}
                          onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
                          onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
                        >
                          Edit metadata
                        </button>
                        <button
                          onClick={handleStartRename}
                          className="flex items-center gap-2 w-full px-4 py-2 text-sm text-left transition-colors"
                          style={{ color: 'var(--text-primary)' }}
                          onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
                          onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
                        >
                          Rename folder
                        </button>
                        <button
                          onClick={() => { setMenuOpen(false); setConfirming(true) }}
                          className="flex items-center gap-2 w-full px-4 py-2 text-sm text-left transition-colors"
                          style={{ color: '#fca5a5' }}
                          onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
                          onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
                        >
                          Delete series
                        </button>
                      </div>
                    )}
                  </div>
                </div>
                {/* Rename inline input */}
                {renaming && (
                  <div className="flex flex-col gap-2 mt-2">
                    <div className="flex gap-2">
                      <input
                        type="text"
                        value={renameName}
                        onChange={(e) => setRenameName(e.target.value)}
                        onKeyDown={(e) => { if (e.key === 'Enter') handleRename(); if (e.key === 'Escape') setRenaming(false) }}
                        className="flex-1 px-3 py-1.5 rounded-lg text-sm min-w-0"
                        style={{ backgroundColor: 'var(--border)', color: 'var(--text-primary)', border: '1px solid var(--border)' }}
                        autoFocus
                      />
                      <button
                        onClick={() => setRenaming(false)}
                        className="px-2 py-1.5 rounded-lg text-sm transition-colors"
                        style={{ color: 'var(--text-secondary)', backgroundColor: 'var(--border)' }}
                      >
                        Cancel
                      </button>
                      <button
                        onClick={handleRename}
                        disabled={renameSaving}
                        className="px-3 py-1.5 rounded-lg text-sm font-medium transition-colors disabled:opacity-50"
                        style={{ backgroundColor: 'var(--accent)', color: '#fff' }}
                      >
                        {renameSaving ? '…' : 'Rename'}
                      </button>
                    </div>
                    {renameError && <p className="text-xs" style={{ color: '#fca5a5' }}>{renameError}</p>}
                  </div>
                )}

                {editingMeta ? (
                  <div className="flex flex-col gap-3 mt-2">
                    <div>
                      <label className="text-xs font-medium mb-1 block" style={{ color: 'var(--text-secondary)' }}>Title</label>
                      <input
                        type="text"
                        value={editForm.title}
                        onChange={(e) => setEditForm((f) => ({ ...f, title: e.target.value }))}
                        className="w-full px-3 py-1.5 rounded-lg text-sm"
                        style={{ backgroundColor: 'var(--border)', color: 'var(--text-primary)', border: '1px solid var(--border)' }}
                        autoFocus
                      />
                    </div>
                    <div>
                      <label className="text-xs font-medium mb-1 block" style={{ color: 'var(--text-secondary)' }}>Year</label>
                      <input
                        type="number"
                        value={editForm.year}
                        onChange={(e) => setEditForm((f) => ({ ...f, year: e.target.value }))}
                        className="w-full px-3 py-1.5 rounded-lg text-sm"
                        style={{ backgroundColor: 'var(--border)', color: 'var(--text-primary)', border: '1px solid var(--border)' }}
                      />
                    </div>
                    <div>
                      <label className="text-xs font-medium mb-1 block" style={{ color: 'var(--text-secondary)' }}>Plot</label>
                      <textarea
                        rows={3}
                        value={editForm.plot}
                        onChange={(e) => setEditForm((f) => ({ ...f, plot: e.target.value }))}
                        className="w-full px-3 py-1.5 rounded-lg text-sm resize-none"
                        style={{ backgroundColor: 'var(--border)', color: 'var(--text-primary)', border: '1px solid var(--border)' }}
                      />
                    </div>
                    <div>
                      <label className="text-xs font-medium mb-1 block" style={{ color: 'var(--text-secondary)' }}>Genres (comma-separated)</label>
                      <input
                        type="text"
                        value={editForm.genres}
                        onChange={(e) => setEditForm((f) => ({ ...f, genres: e.target.value }))}
                        className="w-full px-3 py-1.5 rounded-lg text-sm"
                        style={{ backgroundColor: 'var(--border)', color: 'var(--text-primary)', border: '1px solid var(--border)' }}
                      />
                    </div>
                    <div className="flex gap-2 justify-end">
                      <button
                        onClick={() => setEditingMeta(false)}
                        className="px-3 py-1.5 rounded-lg text-sm transition-colors"
                        style={{ color: 'var(--text-secondary)', backgroundColor: 'var(--border)' }}
                      >
                        Cancel
                      </button>
                      <button
                        onClick={handleSaveSeriesMetadata}
                        disabled={savingMeta}
                        className="px-3 py-1.5 rounded-lg text-sm font-medium transition-colors disabled:opacity-50"
                        style={{ backgroundColor: 'var(--accent)', color: '#fff' }}
                      >
                        {savingMeta ? 'Saving…' : 'Save'}
                      </button>
                    </div>
                  </div>
                ) : (
                  <>
                    {(selectedSeries.year || selectedSeries.genres.length > 0) && (
                      <div className="flex flex-wrap items-center gap-2 mt-1">
                        {selectedSeries.year && <span className="text-xs" style={{ color: 'var(--text-secondary)' }}>{selectedSeries.year}</span>}
                        {selectedSeries.genres.map((g) => (
                          <span key={g} className="text-xs px-1.5 py-0.5 rounded" style={{ backgroundColor: 'var(--border)', color: 'var(--text-secondary)' }}>{g}</span>
                        ))}
                      </div>
                    )}
                    {selectedSeries.plot && (
                      <p className="text-sm mt-2 line-clamp-3" style={{ color: 'var(--text-secondary)' }}>{selectedSeries.plot}</p>
                    )}
                    {selectedSeries.item_key && (
                      <div className="mt-2">
                        <AssignedTagBadges itemKey={selectedSeries.item_key} refreshKey={tagRefreshKey} />
                      </div>
                    )}
                  </>
                )}
              </div>
            </div>
            {/* NFO refresh warning */}
            {warnRefresh && (
              <div
                className="flex items-center gap-3 mt-3 px-3 py-2.5 rounded-lg"
                style={{ backgroundColor: '#78350f33', border: '1px solid #78350f' }}
              >
                <p className="flex-1 text-xs" style={{ color: '#fbbf24' }}>
                  Refreshing from NFO will overwrite your manual edits.
                </p>
                <button
                  onClick={() => setWarnRefresh(false)}
                  className="text-xs px-2 py-1 rounded flex-shrink-0 transition-colors"
                  style={{ color: 'var(--text-secondary)', backgroundColor: 'var(--border)' }}
                >
                  Cancel
                </button>
                <button
                  onClick={doRefreshSeriesMetadata}
                  className="text-xs px-2 py-1 rounded flex-shrink-0 transition-colors"
                  style={{ backgroundColor: '#78350f', color: '#fbbf24' }}
                >
                  Overwrite
                </button>
              </div>
            )}
            {/* Confirmation banner */}
            {confirming && (
              <div
                className="flex items-center gap-3 mt-3 px-3 py-2.5 rounded-lg"
                style={{ backgroundColor: '#7f1d1d33', border: '1px solid #7f1d1d' }}
              >
                <p className="flex-1 text-xs" style={{ color: '#fca5a5' }}>
                  Permanently delete this series and all its files?
                </p>
                <button
                  onClick={() => setConfirming(false)}
                  className="text-xs px-2 py-1 rounded flex-shrink-0 transition-colors"
                  style={{ color: 'var(--text-secondary)', backgroundColor: 'var(--border)' }}
                  onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.color = 'var(--text-primary)')}
                  onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.color = 'var(--text-secondary)')}
                >
                  Cancel
                </button>
                <button
                  onClick={handleDeleteSeries}
                  disabled={deleting}
                  className="text-xs px-2 py-1 rounded flex-shrink-0 transition-colors disabled:opacity-50"
                  style={{ backgroundColor: '#7f1d1d', color: '#fca5a5' }}
                  onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = '#991b1b')}
                  onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = '#7f1d1d')}
                >
                  {deleting ? 'Deleting…' : 'Yes, delete'}
                </button>
              </div>
            )}
          </div>

          {loading ? (
            <SeasonLoadingGrid />
          ) : error ? (
            <ErrorMsg message={error} />
          ) : seasons.length === 0 ? (
            <div className="rounded-lg border p-8 text-center" style={{ borderColor: 'var(--border)', color: 'var(--text-secondary)' }}>
              No seasons found.
            </div>
          ) : (
            <div className="grid gap-4 grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
              {seasons.map((season) => (
                <button
                  key={season.id}
                  onClick={() => openSeason(season, seasons.indexOf(season))}
                  className="group text-left rounded-xl overflow-hidden border transition-all focus:outline-none focus-visible:ring-2"
                  style={{ borderColor: 'var(--border)', backgroundColor: 'var(--surface)' }}
                  onMouseEnter={(e) => {
                    ;(e.currentTarget as HTMLElement).style.borderColor = 'var(--accent)'
                    ;(e.currentTarget as HTMLElement).style.transform = 'translateY(-2px)'
                  }}
                  onMouseLeave={(e) => {
                    ;(e.currentTarget as HTMLElement).style.borderColor = 'var(--border)'
                    ;(e.currentTarget as HTMLElement).style.transform = 'translateY(0)'
                  }}
                >
                  <div className="aspect-[2/3] w-full relative overflow-hidden" style={{ backgroundColor: 'var(--border)' }}>
                    {season.posterUrl ? (
                      // eslint-disable-next-line @next/next/no-img-element
                      <img src={season.posterUrl} alt={season.title} className="absolute inset-0 w-full h-full object-cover" />
                    ) : (
                      <div className="absolute inset-0 flex items-center justify-center text-3xl">📺</div>
                    )}
                  </div>
                  <div className="p-2">
                    <p className="text-xs font-medium truncate" style={{ color: 'var(--text-primary)' }}>
                      {season.title}
                    </p>
                    <p className="text-xs mt-0.5" style={{ color: 'var(--text-secondary)' }}>
                      {season.episodeCount} episode{season.episodeCount !== 1 ? 's' : ''}
                    </p>
                  </div>
                </button>
              ))}
            </div>
          )}
        </div>
      )}

      {view === 'episodes' && selectedSeason && (
        <div className="p-4">
          {loading ? (
            <EpisodeLoadingGrid />
          ) : error ? (
            <ErrorMsg message={error} />
          ) : episodes.length === 0 ? (
            <div className="rounded-lg border p-8 text-center" style={{ borderColor: 'var(--border)', color: 'var(--text-secondary)' }}>
              No episodes found.
            </div>
          ) : (
            <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
              {filteredEpisodes.map((ep) => (
                <EpisodeCard
                  key={ep.id}
                  episode={ep}
                  onClick={() => setPlayingEpisodeIndex(episodes.indexOf(ep))}
                  onTag={() => { setTagPanelItemKey(ep.item_key!); setTagPanelDisabled(false); setShowTagPanel(true) }}
                  downloadUrl={`/api/file?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(ep.videoPath)}`}
                  onDelete={() => {
                    fetch(
                      `/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(selectedSeries!.id)}&episodeKey=${encodeURIComponent(ep.item_key!)}`,
                      { method: 'DELETE' }
                    ).then(() => {
                      setEpisodes((prev) => prev.filter((e) => e.id !== ep.id))
                    })
                  }}
                  onRename={async (newName) => {
                    const res = await fetch('/api/rename', {
                      method: 'POST',
                      headers: { 'Content-Type': 'application/json' },
                      body: JSON.stringify({ libraryId, oldPath: ep.videoPath, newName, itemType: 'tv_episode' }),
                    })
                    if (!res.ok) return false
                    // Refetch episodes to get updated data
                    const seasonId = selectedSeason!.id
                    const data = await fetch(
                      `/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(selectedSeries!.id)}&seasonId=${encodeURIComponent(seasonId)}`
                    ).then((r) => r.json())
                    setEpisodes(data)
                    return true
                  }}
                />
              ))}
            </div>
          )}
        </div>
      )}
                </div>
              </div>

              {/* Floating controls — tag + close */}
              <div className="absolute top-4 right-4 z-10 flex items-center gap-1.5" onClick={(e) => e.stopPropagation()}>
                {view === 'seasons' && selectedSeries?.item_key && !showTagPanel && !readOnly && (
                  <button
                    onClick={() => { setShowTagPanel(true); setTagPanelItemKey(selectedSeries.item_key!); setTagPanelDisabled(false) }}
                    className={smallBtn}
                    style={{ backgroundColor: 'var(--surface)', color: 'var(--text-primary)' }}
                    onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface-hover)')}
                    onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface)')}
                    aria-label="Show tags"
                    title="Tags"
                  >
                    🏷
                  </button>
                )}
                <button
                  onClick={goToSeries}
                  className={smallBtn}
                  style={{ backgroundColor: 'var(--surface)', color: 'var(--text-primary)' }}
                  onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface-hover)')}
                  onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--surface)')}
                  aria-label="Close"
                >
                  ✕
                </button>
              </div>

              {/* Prev — series in seasons view, season in episodes view */}
              {(view === 'seasons'
                  ? selectedSeriesIndex !== null && selectedSeriesIndex > 0
                  : selectedSeasonIndex !== null && selectedSeasonIndex > 0) && (
                <button
                  onClick={(e) => {
                    e.stopPropagation()
                    if (view === 'seasons') openSeries(filteredSeries[selectedSeriesIndex! - 1])
                    else openSeason(seasons[selectedSeasonIndex! - 1], selectedSeasonIndex! - 1)
                  }}
                  className="absolute left-2 top-1/2 -translate-y-1/2 z-10 w-12 h-12 rounded-full flex items-center justify-center text-lg transition-opacity hover:opacity-100 opacity-70"
                  style={{ backgroundColor: 'rgba(0,0,0,0.4)', color: '#fff' }}
                  aria-label="Previous"
                >
                  ‹
                </button>
              )}

              {/* Next — series in seasons view, season in episodes view */}
              {(view === 'seasons'
                  ? selectedSeriesIndex !== null && selectedSeriesIndex < filteredSeries.length - 1
                  : selectedSeasonIndex !== null && selectedSeasonIndex < seasons.length - 1) && (
                <button
                  onClick={(e) => {
                    e.stopPropagation()
                    if (view === 'seasons') openSeries(filteredSeries[selectedSeriesIndex! + 1])
                    else openSeason(seasons[selectedSeasonIndex! + 1], selectedSeasonIndex! + 1)
                  }}
                  className="absolute right-2 top-1/2 -translate-y-1/2 z-10 w-12 h-12 rounded-full flex items-center justify-center text-lg transition-opacity hover:opacity-100 opacity-70"
                  style={{ backgroundColor: 'rgba(0,0,0,0.4)', color: '#fff' }}
                  aria-label="Next"
                >
                  ›
                </button>
              )}
            </div>

            {/* Right tag panel */}
            {showTagPanel && (
              <MediaTagPanel
                itemKey={tagPanelItemKey ?? ''}
                onHide={() => setShowTagPanel(false)}
                onClose={goToSeries}
                onTagsChanged={() => {
                  setTagRefreshKey((k) => k + 1)
                  setFilterRefreshKey((k) => k + 1)
                  fetchAssignments()
                  fetchSeriesEpisodeTags()
                }}
                externalRefreshKey={tagRefreshKey}
                disabled={tagPanelDisabled}
                disabledMessage="Seasons cannot be tagged. Select an episode to tag it."
                readOnly={readOnly}
              />
            )}
          </div>
        </div>
      )}
    </div>
  )
}

function ErrorMsg({ message }: { message: string }) {
  return (
    <div className="rounded-lg border p-8 text-center" style={{ borderColor: 'var(--border)', color: 'var(--text-secondary)' }}>
      {message}
    </div>
  )
}

function SeriesLoadingGrid() {
  return (
    <div className="grid gap-4 grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
      {Array.from({ length: 12 }).map((_, i) => (
        <div key={i} className="rounded-xl overflow-hidden" style={{ backgroundColor: 'var(--surface)' }}>
          <div className="aspect-[2/3] w-full animate-pulse" style={{ backgroundColor: 'var(--border)' }} />
          <div className="p-2">
            <div className="h-3 rounded animate-pulse" style={{ backgroundColor: 'var(--border)', width: '70%' }} />
          </div>
        </div>
      ))}
    </div>
  )
}

function SeasonLoadingGrid() {
  return (
    <div className="grid gap-4 grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
      {Array.from({ length: 6 }).map((_, i) => (
        <div key={i} className="rounded-xl overflow-hidden" style={{ backgroundColor: 'var(--surface)' }}>
          <div className="aspect-[2/3] w-full animate-pulse" style={{ backgroundColor: 'var(--border)' }} />
          <div className="p-2">
            <div className="h-3 rounded animate-pulse" style={{ backgroundColor: 'var(--border)', width: '60%' }} />
          </div>
        </div>
      ))}
    </div>
  )
}

function EpisodeLoadingGrid() {
  return (
    <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
      {Array.from({ length: 8 }).map((_, i) => (
        <div key={i} className="rounded-xl overflow-hidden" style={{ backgroundColor: 'var(--surface)' }}>
          <div className="aspect-video w-full animate-pulse" style={{ backgroundColor: 'var(--border)' }} />
          <div className="p-2">
            <div className="h-3 rounded animate-pulse" style={{ backgroundColor: 'var(--border)', width: '80%' }} />
          </div>
        </div>
      ))}
    </div>
  )
}
