import { useEffect, useMemo, useRef, useState } from 'react'
import {
  useTmdbTrending,
  useTmdbUpcoming,
  useTmdbTopRatedMovies,
  useTmdbTopRatedTv,
  useTmdbDiscoverMovies,
  useTmdbDiscoverTv,
} from '../../../hooks/use-tmdb'
import { useLibraryByTmdbId, useLibraryItems } from '../../../hooks/use-jellyfin'
import { useWikidataAwardWinners } from '../../../hooks/use-external'
import { useCanonListResolved } from '../../../hooks/use-canon-list'
import { topGenre } from '../../../lib/top-genre'
import { tmdbMovieGenreId, tmdbTvGenreId } from '../../../lib/tmdb-genres'
import { mapTmdbToJf } from '../../../lib/tmdb-mapping'
import { regionForUser } from '../../../lib/format'
import ContentRow from '../../../components/ui/ContentRow'
import { usePreferencesStore } from '../../../stores/preferences-store'

/**
 * Trending today row. Companion to weekly trending - daily catches the buzz,
 * weekly smooths it out.
 */
export function TrendingTodayRow() {
  const trending = useTmdbTrending('all', 'day')
  const libraryByTmdbId = useLibraryByTmdbId()
  const hideAdult = usePreferencesStore(s => s.hideAdult)
  const items = useMemo(() => {
    const raw = trending.data?.results || []
    const filtered = raw.filter(r => !hideAdult || !r.adult)
    return mapTmdbToJf(filtered, libraryByTmdbId.data)
  }, [trending.data, libraryByTmdbId.data, hideAdult])
  if (items.length === 0) return null
  return (
    <ContentRow
      title="Trending today"
      subtitle="What's getting attention right now on TMDB"
      items={items}
      layoutKey="trending_today"
    />
  )
}

/**
 * Critically acclaimed missing. TMDB top-rated movies and series filtered
 * against the library - only items NOT in the library show up.
 */
export function CriticallyAcclaimedMissingRow() {
  const movies = useTmdbTopRatedMovies()
  const tv = useTmdbTopRatedTv()
  const libraryByTmdbId = useLibraryByTmdbId()
  const items = useMemo(() => {
    const lib = libraryByTmdbId.data
    if (!lib) return []
    const movieList = (movies.data?.results || [])
      .map(m => ({ ...m, media_type: 'movie' }))
      .filter(m => !lib.has(String(m.id)))
    const tvList = (tv.data?.results || [])
      .map(m => ({ ...m, media_type: 'tv' }))
      .filter(m => !lib.has(String(m.id)))
    // Interleave so the row mixes both types instead of 20 movies then 20 series.
    const out: any[] = []
    const max = Math.max(movieList.length, tvList.length)
    for (let i = 0; i < max && out.length < 20; i++) {
      if (movieList[i]) out.push(movieList[i])
      if (tvList[i]) out.push(tvList[i])
    }
    return mapTmdbToJf(out, lib)
  }, [movies.data, tv.data, libraryByTmdbId.data])
  if (items.length === 0) return null
  return (
    <ContentRow
      title="Critically acclaimed - missing"
      subtitle="Top-rated on TMDB and not yet in your library"
      items={items}
      layoutKey="critically_acclaimed_missing"
    />
  )
}

/**
 * Genre deep-dive. Tallies the user's recently watched library genres, picks
 * the leader, surfaces TMDB-discover canon in that genre filtered to items
 * not in the library.
 */
export function GenreDeepDiveRow() {
  const recentlyPlayed = useLibraryItems(undefined, {
    includeItemTypes: ['Movie', 'Series'],
    sortBy: ['DatePlayed'],
    sortOrder: ['Descending'],
    filters: ['IsPlayed'],
    limit: 30,
  })
  const top = topGenre(recentlyPlayed.data?.Items)
  const movieGenreId = top.primary ? tmdbMovieGenreId(top.primary) : null
  const tvGenreId = top.primary ? tmdbTvGenreId(top.primary) : null

  const movieDiscover = useTmdbDiscoverMovies(
    movieGenreId
      ? {
          with_genres: String(movieGenreId),
          'vote_count.gte': '1500',
          sort_by: 'vote_average.desc',
        }
      : ({} as Record<string, string>),
  )
  const tvDiscover = useTmdbDiscoverTv(
    tvGenreId
      ? {
          with_genres: String(tvGenreId),
          'vote_count.gte': '500',
          sort_by: 'vote_average.desc',
        }
      : ({} as Record<string, string>),
  )

  const libraryByTmdbId = useLibraryByTmdbId()
  const items = useMemo(() => {
    const lib = libraryByTmdbId.data
    if (!lib || !top.primary) return []
    const movies = (movieDiscover.data?.results || [])
      .filter(m => !lib.has(String(m.id)))
      .map(m => ({ ...m, media_type: 'movie' }))
    const tv = (tvDiscover.data?.results || [])
      .filter(m => !lib.has(String(m.id)))
      .map(m => ({ ...m, media_type: 'tv' }))
    const out: any[] = []
    const max = Math.max(movies.length, tv.length)
    for (let i = 0; i < max && out.length < 18; i++) {
      if (movies[i]) out.push(movies[i])
      if (tv[i]) out.push(tv[i])
    }
    return mapTmdbToJf(out, lib)
  }, [movieDiscover.data, tvDiscover.data, libraryByTmdbId.data, top.primary])

  if (!top.primary || items.length === 0) return null
  return (
    <ContentRow
      title={`Your top genre: ${top.primary}`}
      subtitle="Canon you haven't picked up yet"
      items={items}
      layoutKey={`genre_deep_${top.primary}`}
    />
  )
}

/**
 * Cult classics. Highly rated films with enough votes to be canon but capped
 * popularity so we surface the cult tier rather than blockbusters.
 */
export function CultClassicsRow() {
  const discover = useTmdbDiscoverMovies({
    'vote_count.gte': '10000',
    'vote_average.gte': '8',
    'popularity.lte': '20',
    sort_by: 'vote_average.desc',
  })
  const libraryByTmdbId = useLibraryByTmdbId()
  const items = useMemo(() => {
    const raw = (discover.data?.results || []).map(m => ({ ...m, media_type: 'movie' }))
    return mapTmdbToJf(raw, libraryByTmdbId.data)
  }, [discover.data, libraryByTmdbId.data])
  if (items.length === 0) return null
  return (
    <ContentRow
      title="Cult classics"
      subtitle="Highly rated films flying under the radar"
      items={items}
      layoutKey="cult_classics"
    />
  )
}

/**
 * Year-end best-ofs. Top-rated movies released in the current year.
 */
export function YearEndBestOfRow() {
  const year = new Date().getFullYear()
  const discover = useTmdbDiscoverMovies({
    primary_release_year: String(year),
    'vote_count.gte': '200',
    sort_by: 'vote_average.desc',
  })
  const libraryByTmdbId = useLibraryByTmdbId()
  const items = useMemo(() => {
    const raw = (discover.data?.results || []).map(m => ({ ...m, media_type: 'movie' }))
    return mapTmdbToJf(raw, libraryByTmdbId.data)
  }, [discover.data, libraryByTmdbId.data])
  if (items.length === 0) return null
  return (
    <ContentRow
      title={`Top movies of ${year}`}
      subtitle="The best-rated theatrical and streaming releases this year"
      items={items}
      layoutKey={`year_end_${year}`}
    />
  )
}

/**
 * Foreign cinema. Drops English originals client-side from the broad
 * rated-discover pool.
 */
export function ForeignCinemaRow() {
  const discover = useTmdbDiscoverMovies({
    'vote_count.gte': '500',
    sort_by: 'vote_average.desc',
  })
  const libraryByTmdbId = useLibraryByTmdbId()
  const items = useMemo(() => {
    const raw = (discover.data?.results || [])
      .filter((m: any) => m.original_language && m.original_language !== 'en')
      .slice(0, 20)
      .map(m => ({ ...m, media_type: 'movie' }))
    return mapTmdbToJf(raw, libraryByTmdbId.data)
  }, [discover.data, libraryByTmdbId.data])
  if (items.length === 0) return null
  return (
    <ContentRow
      title="Foreign cinema"
      subtitle="Top-rated non-English films"
      items={items}
      layoutKey="foreign_cinema"
    />
  )
}

/**
 * Documentary picks. Genre 99 = Documentary in TMDB.
 */
export function DocumentaryPicksRow() {
  const discover = useTmdbDiscoverMovies({
    with_genres: '99',
    'vote_count.gte': '300',
    sort_by: 'vote_average.desc',
  })
  const libraryByTmdbId = useLibraryByTmdbId()
  const items = useMemo(() => {
    const raw = (discover.data?.results || []).map(m => ({ ...m, media_type: 'movie' }))
    return mapTmdbToJf(raw, libraryByTmdbId.data)
  }, [discover.data, libraryByTmdbId.data])
  if (items.length === 0) return null
  return (
    <ContentRow
      title="Documentary picks"
      subtitle="The best-rated docs on TMDB"
      items={items}
      layoutKey="documentary_picks"
    />
  )
}

/**
 * Award winners you're missing. Wikidata Q-id 102427 is "Academy Award for
 * Best Picture"; the SPARQL gives us all winners with TMDB cross-refs.
 */
const ACADEMY_AWARD_BEST_PICTURE = 'Q102427'

export function AwardWinnersMissingRow() {
  // Lazy-mount on scroll - sits deep in the home page, don't fire 18 TMDB
  // lookups on first paint.
  const containerRef = useRef<HTMLDivElement>(null)
  const [near, setNear] = useState(false)
  useEffect(() => {
    if (!containerRef.current) return
    const el = containerRef.current
    const obs = new IntersectionObserver(
      entries => {
        for (const e of entries) {
          if (e.isIntersecting) {
            setNear(true)
            obs.disconnect()
            return
          }
        }
      },
      { rootMargin: '200px' },
    )
    obs.observe(el)
    return () => obs.disconnect()
  }, [])
  return <div ref={containerRef}>{near ? <AwardWinnersMissingRowMounted /> : null}</div>
}

function AwardWinnersMissingRowMounted() {
  const winners = useWikidataAwardWinners(ACADEMY_AWARD_BEST_PICTURE)
  const libraryByTmdbId = useLibraryByTmdbId()
  // Wikidata gives us TMDB ids + labels but no posters. Resolve them so the
  // row renders real artwork instead of letter placeholders.
  const missingIds = useMemo(() => {
    const list = winners.data || []
    const lib = libraryByTmdbId.data
    if (!lib) return [] as number[]
    return list
      .filter(w => w.tmdbId && w.type === 'movie' && !lib.has(w.tmdbId))
      .slice(0, 18)
      .map(w => Number(w.tmdbId))
      .filter(n => Number.isFinite(n))
  }, [winners.data, libraryByTmdbId.data])
  const { items: tmdbItems } = useCanonListResolved(missingIds)
  const items = useMemo(
    () => mapTmdbToJf(tmdbItems, libraryByTmdbId.data),
    [tmdbItems, libraryByTmdbId.data],
  )
  if (items.length === 0) return null
  return (
    <ContentRow
      title="Best Picture winners you're missing"
      subtitle="Academy Awards canon, filtered against your library"
      items={items}
      layoutKey="award_winners_missing"
    />
  )
}

/**
 * Coming soon row. TMDB upcoming movies, region-filtered.
 */
export function ComingSoonRow() {
  const prefs = usePreferencesStore()
  const region = prefs.region || regionForUser()
  const upcoming = useTmdbUpcoming(region)
  const libraryByTmdbId = useLibraryByTmdbId()
  const items = useMemo(() => {
    const raw = (upcoming.data?.results || []).map(m => ({ ...m, media_type: 'movie' }))
    return mapTmdbToJf(raw, libraryByTmdbId.data)
  }, [upcoming.data, libraryByTmdbId.data])
  if (items.length === 0) return null
  return (
    <ContentRow
      title="Coming soon"
      subtitle={`New theatrical and streaming releases${region ? ` in ${region}` : ''}`}
      items={items}
      layoutKey="coming_soon"
    />
  )
}
