import { useMemo } from 'react'
import { useLibraryByTmdbId } from './use-jellyfin'
import { useRadarrLibrary, useSonarrLibrary, useRadarrQueue, useSonarrQueue } from './use-arr'

/**
 * Unified per-item availability state. Combines what Jellyfin already
 * has with what Sonarr/Radarr know about so the UI can label every
 * recommendation with one of:
 *   - 'available'     - playable in Jellyfin right now
 *   - 'partial'       - in Sonarr with some episodes downloaded but not all
 *   - 'processing'    - being downloaded by *arr at this moment
 *   - 'pending'       - in *arr, monitored, but no file yet (waiting on
 *                       indexer / release)
 *   - 'requested'     - in *arr, monitored, but inactive
 *   - 'missing'       - not in any system
 */

export type Availability = 'available' | 'partial' | 'processing' | 'pending' | 'requested' | 'missing'

export interface AvailabilityRecord {
  status: Availability
  /** Filled when the item lives in Sonarr/Radarr - useful for cancel /
   *  search-now actions on the Requests page. */
  arrId?: number
  arrKind?: 'sonarr' | 'radarr'
  arrTier?: 'default' | '4k'
  /** Local Jellyfin id when matched. */
  jellyfinId?: string
}

/**
 * Compute the availability map keyed by TMDB id. Reads all four
 * regular+4K *arr libraries plus their queues. The map is stable as
 * long as the upstream queries don't change so consumers can pass it
 * through `useMemo` cheaply.
 */
export function useAvailability() {
  const lib = useLibraryByTmdbId()
  const radarrA = useRadarrLibrary('default')
  const radarrB = useRadarrLibrary('4k')
  const sonarrA = useSonarrLibrary('default')
  const sonarrB = useSonarrLibrary('4k')
  const radarrQA = useRadarrQueue('default')
  const radarrQB = useRadarrQueue('4k')
  const sonarrQA = useSonarrQueue('default')
  const sonarrQB = useSonarrQueue('4k')

  const map = useMemo(() => {
    const out = new Map<string, AvailabilityRecord>()

    // Jellyfin first - "available" beats everything *arr might say.
    if (lib.data) {
      for (const [tmdbId, info] of lib.data) {
        out.set(tmdbId, { status: 'available', jellyfinId: info.id })
      }
    }

    function applyMovieList(
      list: { id?: number; tmdbId: number; hasFile?: boolean; monitored?: boolean }[] | null | undefined,
      tier: 'default' | '4k',
      queue: { records?: { movieId?: number }[] } | null | undefined,
    ) {
      if (!list) return
      const queueIds = new Set((queue?.records || []).map(r => r.movieId).filter((x): x is number => x != null))
      for (const m of list) {
        if (!m.tmdbId) continue
        const key = String(m.tmdbId)
        const existing = out.get(key)
        if (existing && existing.status === 'available') continue
        let status: Availability
        if (m.hasFile) status = 'available'
        else if (m.id != null && queueIds.has(m.id)) status = 'processing'
        else if (m.monitored) status = 'pending'
        else status = 'requested'
        out.set(key, { status, arrId: m.id, arrKind: 'radarr', arrTier: tier })
      }
    }

    function applySeriesList(
      list: { id?: number; tmdbId?: number; tvdbId?: number; imdbId?: string | null; seasons?: { monitored: boolean; statistics?: { episodeFileCount: number; episodeCount: number } }[] }[] | null | undefined,
      tier: 'default' | '4k',
      queue: { records?: { seriesId?: number }[] } | null | undefined,
    ) {
      if (!list) return
      const queueIds = new Set((queue?.records || []).map(r => r.seriesId).filter((x): x is number => x != null))
      for (const s of list) {
        const tmdb = s.tmdbId ? String(s.tmdbId) : null
        if (!tmdb) continue
        const existing = out.get(tmdb)
        if (existing && existing.status === 'available') continue
        let status: Availability
        const seasons = s.seasons || []
        const totalEps = seasons.reduce((acc, x) => acc + (x.statistics?.episodeCount || 0), 0)
        const haveEps = seasons.reduce((acc, x) => acc + (x.statistics?.episodeFileCount || 0), 0)
        if (s.id != null && queueIds.has(s.id)) {
          status = 'processing'
        } else if (totalEps > 0 && haveEps >= totalEps) {
          status = 'available'
        } else if (haveEps > 0) {
          status = 'partial'
        } else if (seasons.some(x => x.monitored)) {
          status = 'pending'
        } else {
          status = 'requested'
        }
        out.set(tmdb, { status, arrId: s.id, arrKind: 'sonarr', arrTier: tier })
      }
    }

    applyMovieList(radarrA.data, 'default', radarrQA.data)
    applyMovieList(radarrB.data, '4k', radarrQB.data)
    applySeriesList(sonarrA.data, 'default', sonarrQA.data)
    applySeriesList(sonarrB.data, '4k', sonarrQB.data)

    return out
  }, [
    lib.data,
    radarrA.data,
    radarrB.data,
    sonarrA.data,
    sonarrB.data,
    radarrQA.data,
    radarrQB.data,
    sonarrQA.data,
    sonarrQB.data,
  ])

  return {
    map,
    isLoading: lib.isLoading || radarrA.isLoading || radarrB.isLoading || sonarrA.isLoading || sonarrB.isLoading,
  }
}

/**
 * Single-item availability lookup. Convenience over reading from the
 * full map directly when a component only cares about one tmdb id.
 */
export function useItemAvailability(tmdbId: string | number | null | undefined): AvailabilityRecord | null {
  const { map } = useAvailability()
  if (tmdbId == null) return null
  return map.get(String(tmdbId)) || null
}
