import { useMemo } from 'react'
import { motion } from 'framer-motion'
import { useNavigate } from 'react-router-dom'
import { useQueryClient } from '@tanstack/react-query'
import { Database, RefreshCw, Trash2, Clock, Check, AlertCircle, Settings } from '../lib/icons'
import { useArrInstances } from '../stores/arr-instances-store'
import { useRadarrLibrary, useSonarrLibrary, useRadarrQueue, useSonarrQueue } from '../hooks/use-arr'
import { radarrClient, type RadarrMovie, type RadarrQueueItem } from '../api/radarr'
import { sonarrClient, type SonarrSeries, type SonarrQueueItem } from '../api/sonarr'

/**
 * Aggregated requests view: every movie + show currently sitting in
 * Sonarr/Radarr but not fully available yet, with per-item actions.
 *
 * Data sources:
 *   - Radarr/Sonarr libraries (filter to monitored + missing/partial)
 *   - Their queues (in-flight downloads with progress)
 *
 * Hides itself with a setup prompt when no *arr instance is configured.
 */

type Tier = 'default' | '4k'

interface AggregatedRequest {
  key: string
  kind: 'movie' | 'tv'
  tier: Tier
  arrId: number
  title: string
  year?: number | null
  poster?: string | null
  /** Render label like "Downloading 64%" or "Pending release". */
  state: 'processing' | 'pending' | 'partial' | 'requested'
  detail?: string
  /** Optional progress 0..100 when processing. */
  progress?: number
}

export default function RequestsPage() {
  const navigate = useNavigate()
  const radarr = useArrInstances(s => s.pick('radarr', 'default'))
  const radarr4k = useArrInstances(s => s.pick('radarr', '4k'))
  const sonarr = useArrInstances(s => s.pick('sonarr', 'default'))
  const sonarr4k = useArrInstances(s => s.pick('sonarr', '4k'))

  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 aggregated: AggregatedRequest[] = useMemo(() => {
    const out: AggregatedRequest[] = []
    function pushMovies(list: RadarrMovie[] | null | undefined, queue: { records?: RadarrQueueItem[] } | null | undefined, tier: Tier) {
      if (!list) return
      const queueByMovieId = new Map<number, RadarrQueueItem>()
      for (const q of queue?.records || []) if (q.movieId != null) queueByMovieId.set(q.movieId, q)
      for (const m of list) {
        if (m.hasFile) continue
        if (!m.id) continue
        const q = queueByMovieId.get(m.id)
        const poster = m.images?.find(x => x.coverType === 'poster')?.remoteUrl || null
        if (q) {
          const total = (q.size ?? 0)
          const left = (q.sizeleft ?? 0)
          const progress = total > 0 ? Math.round(((total - left) / total) * 100) : undefined
          out.push({
            key: `radarr-${tier}-${m.id}`,
            kind: 'movie',
            tier,
            arrId: m.id,
            title: m.title,
            year: m.year,
            poster,
            state: 'processing',
            detail: q.timeleft ? `${progress ?? 0}% · ${q.timeleft} left` : `${progress ?? 0}%`,
            progress,
          })
        } else if (m.monitored) {
          out.push({
            key: `radarr-${tier}-${m.id}`,
            kind: 'movie',
            tier,
            arrId: m.id,
            title: m.title,
            year: m.year,
            poster,
            state: 'pending',
            detail: m.status === 'announced' ? 'Announced' : m.status === 'inCinemas' ? 'In cinemas' : 'Searching for release',
          })
        } else {
          out.push({
            key: `radarr-${tier}-${m.id}`,
            kind: 'movie',
            tier,
            arrId: m.id,
            title: m.title,
            year: m.year,
            poster,
            state: 'requested',
            detail: 'Not monitored',
          })
        }
      }
    }
    function pushSeries(list: SonarrSeries[] | null | undefined, queue: { records?: SonarrQueueItem[] } | null | undefined, tier: Tier) {
      if (!list) return
      const queueBySeriesId = new Map<number, SonarrQueueItem[]>()
      for (const q of queue?.records || []) {
        if (q.seriesId == null) continue
        const existing = queueBySeriesId.get(q.seriesId) || []
        queueBySeriesId.set(q.seriesId, [...existing, q])
      }
      for (const s of list) {
        if (!s.id) continue
        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 (totalEps > 0 && haveEps >= totalEps) continue
        const poster = s.images?.find(x => x.coverType === 'poster')?.remoteUrl || null
        const q = queueBySeriesId.get(s.id)
        const monitoredSeasons = seasons.filter(x => x.monitored).length
        if (q && q.length > 0) {
          out.push({
            key: `sonarr-${tier}-${s.id}`,
            kind: 'tv',
            tier,
            arrId: s.id,
            title: s.title,
            year: s.year,
            poster,
            state: 'processing',
            detail: `${q.length} episode${q.length === 1 ? '' : 's'} downloading`,
          })
        } else if (haveEps > 0 && haveEps < totalEps) {
          out.push({
            key: `sonarr-${tier}-${s.id}`,
            kind: 'tv',
            tier,
            arrId: s.id,
            title: s.title,
            year: s.year,
            poster,
            state: 'partial',
            detail: `${haveEps} of ${totalEps} episodes`,
            progress: totalEps > 0 ? Math.round((haveEps / totalEps) * 100) : undefined,
          })
        } else if (monitoredSeasons > 0) {
          out.push({
            key: `sonarr-${tier}-${s.id}`,
            kind: 'tv',
            tier,
            arrId: s.id,
            title: s.title,
            year: s.year,
            poster,
            state: 'pending',
            detail: `${monitoredSeasons} season${monitoredSeasons === 1 ? '' : 's'} monitored`,
          })
        } else {
          out.push({
            key: `sonarr-${tier}-${s.id}`,
            kind: 'tv',
            tier,
            arrId: s.id,
            title: s.title,
            year: s.year,
            poster,
            state: 'requested',
            detail: 'No seasons monitored',
          })
        }
      }
    }
    pushMovies(radarrA.data, radarrQA.data, 'default')
    pushMovies(radarrB.data, radarrQB.data, '4k')
    pushSeries(sonarrA.data, sonarrQA.data, 'default')
    pushSeries(sonarrB.data, sonarrQB.data, '4k')

    // Sort: processing first, then partial, pending, requested. Within
    // each bucket, alphabetical.
    const order: Record<AggregatedRequest['state'], number> = { processing: 0, partial: 1, pending: 2, requested: 3 }
    return out.sort((a, b) => {
      const oa = order[a.state]
      const ob = order[b.state]
      if (oa !== ob) return oa - ob
      return a.title.localeCompare(b.title)
    })
  }, [radarrA.data, radarrB.data, sonarrA.data, sonarrB.data, radarrQA.data, radarrQB.data, sonarrQA.data, sonarrQB.data])

  const noInstance = !radarr && !radarr4k && !sonarr && !sonarr4k

  const counts = useMemo(() => {
    const out = { processing: 0, partial: 0, pending: 0, requested: 0 }
    for (const r of aggregated) out[r.state]++
    return out
  }, [aggregated])

  return (
    <div className="px-7 pt-4 pb-12">
      <header className="mb-7 flex flex-col md:flex-row md:items-end md:justify-between gap-4">
        <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]">Requests</span>
          </div>
          <h1 className="text-3xl md:text-4xl font-bold font-display text-text-1 tracking-tight">
            Pending downloads
          </h1>
          <p className="text-[13px] text-text-3 mt-1.5">
            Items currently in your *arr stack that haven't finished landing in Jellyfin.
          </p>
        </div>
        {!noInstance && aggregated.length > 0 && (
          <div className="flex items-center gap-1.5 flex-wrap">
            {counts.processing > 0 && <CountChip tone="blue" label="Downloading" value={counts.processing} />}
            {counts.partial > 0 && <CountChip tone="amber" label="Partial" value={counts.partial} />}
            {counts.pending > 0 && <CountChip tone="purple" label="Pending" value={counts.pending} />}
            {counts.requested > 0 && <CountChip tone="neutral" label="Requested" value={counts.requested} />}
          </div>
        )}
      </header>

      {noInstance ? (
        <div className="rounded-xl bg-elevated/30 ring-1 ring-border p-10 text-center">
          <div className="relative w-14 h-14 mx-auto mb-4">
            <div className="absolute inset-0 rounded-full bg-accent/10 blur-xl" />
            <div className="relative w-full h-full rounded-full bg-gradient-to-br from-elevated to-surface ring-1 ring-border grid place-items-center">
              <Database size={20} className="text-accent" />
            </div>
          </div>
          <p className="text-[15px] text-text-1 font-semibold tracking-tight mb-1.5">No Sonarr or Radarr connected</p>
          <p className="text-[12.5px] text-text-3 max-w-md mx-auto leading-relaxed mb-5">
            Connect a Sonarr or Radarr instance to track active downloads, monitor pending releases, and manage requests here.
          </p>
          <button
            onClick={() => navigate('/settings#sonarr-radarr')}
            className="inline-flex items-center gap-1.5 h-10 px-5 rounded-full bg-accent hover:bg-accent-hover text-void text-[12.5px] font-semibold tracking-tight transition shadow-[0_8px_24px_-8px_rgba(245,182,66,0.5)] focus-ring"
          >
            <Settings size={12} stroke={2} />
            Open Settings
          </button>
        </div>
      ) : aggregated.length === 0 ? (
        <div className="rounded-xl bg-elevated/30 ring-1 ring-border p-10 text-center">
          <div className="relative w-14 h-14 mx-auto mb-4">
            <div className="absolute inset-0 rounded-full bg-success/10 blur-xl" />
            <div className="relative w-full h-full rounded-full bg-gradient-to-br from-elevated to-surface ring-1 ring-success/30 grid place-items-center">
              <Check size={18} className="text-success" stroke={2.5} />
            </div>
          </div>
          <p className="text-[15px] text-text-1 font-semibold tracking-tight mb-1">All caught up</p>
          <p className="text-[12.5px] text-text-3 max-w-sm mx-auto leading-relaxed">
            Every monitored item in your *arr stack is fully downloaded.
          </p>
        </div>
      ) : (
        <ul className="space-y-2">
          {aggregated.map((req, i) => (
            <RequestRow key={req.key} req={req} index={i} />
          ))}
        </ul>
      )}
    </div>
  )
}

function RequestRow({ req, index }: { req: AggregatedRequest; index: number }) {
  const qc = useQueryClient()
  const radarrInstance = useArrInstances(s => s.pick('radarr', req.tier))
  const sonarrInstance = useArrInstances(s => s.pick('sonarr', req.tier))

  async function searchNow() {
    if (req.kind === 'movie' && radarrInstance) {
      await radarrClient(radarrInstance).searchMovie(req.arrId)
      qc.invalidateQueries({ queryKey: ['radarr', 'queue'] })
    } else if (req.kind === 'tv' && sonarrInstance) {
      await sonarrClient(sonarrInstance).searchSeries(req.arrId)
      qc.invalidateQueries({ queryKey: ['sonarr', 'queue'] })
    }
  }

  async function cancel() {
    const ok = confirm(`Remove "${req.title}" from ${req.kind === 'movie' ? 'Radarr' : 'Sonarr'}? Files on disk will be left in place.`)
    if (!ok) return
    if (req.kind === 'movie' && radarrInstance) {
      await radarrClient(radarrInstance).removeMovie(req.arrId, false)
      qc.invalidateQueries({ queryKey: ['radarr', 'library'] })
    } else if (req.kind === 'tv' && sonarrInstance) {
      await sonarrClient(sonarrInstance).removeSeries(req.arrId, false)
      qc.invalidateQueries({ queryKey: ['sonarr', 'library'] })
    }
  }

  return (
    <motion.li
      initial={{ opacity: 0, y: 6 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.25, delay: Math.min(index * 0.02, 0.3) }}
      className="flex items-center gap-3 p-3 rounded-lg bg-elevated/40 ring-1 ring-border hover:ring-border-strong transition"
    >
      <div className="shrink-0 w-12 aspect-[2/3] rounded bg-black overflow-hidden ring-1 ring-border">
        {req.poster && (
          <img src={req.poster} alt="" className="w-full h-full object-cover" loading="lazy" />
        )}
      </div>
      <div className="min-w-0 flex-1">
        <div className="flex items-center gap-2 flex-wrap mb-1">
          <p className="text-[13px] font-medium text-text-1 truncate tracking-tight">{req.title}</p>
          {req.year && <span className="text-[11px] text-text-4 tabular-nums">{req.year}</span>}
          <StateChip state={req.state} />
          {req.tier === '4k' && (
            <span className="inline-flex items-center h-[16px] px-1.5 rounded text-[9px] font-bold uppercase tracking-[0.06em] bg-cool/20 text-cool ring-1 ring-cool/30">
              4K
            </span>
          )}
        </div>
        {req.detail && (
          <p className="text-[11.5px] text-text-3">{req.detail}</p>
        )}
        {req.progress != null && req.progress >= 0 && req.progress <= 100 && (
          <div className="mt-1.5 h-1 rounded-full bg-elevated overflow-hidden max-w-md">
            <div
              className="h-full bg-accent transition-[width] duration-300"
              style={{ width: `${req.progress}%` }}
            />
          </div>
        )}
      </div>
      <div className="shrink-0 flex items-center gap-1">
        <button
          onClick={searchNow}
          title="Search now"
          aria-label="Search now"
          className="w-9 h-9 grid place-items-center rounded-full text-text-3 hover:text-accent hover:bg-elevated transition focus-ring"
        >
          <RefreshCw size={13} stroke={2} />
        </button>
        <button
          onClick={cancel}
          title="Remove from *arr"
          aria-label="Remove"
          className="w-9 h-9 grid place-items-center rounded-full text-text-3 hover:text-red-300 hover:bg-elevated transition focus-ring"
        >
          <Trash2 size={13} stroke={2} />
        </button>
      </div>
    </motion.li>
  )
}

function CountChip({ tone, label, value }: { tone: 'blue' | 'amber' | 'purple' | 'neutral'; label: string; value: number }) {
  const palette =
    tone === 'blue' ? 'bg-blue-500/12 text-blue-200 ring-blue-400/30'
    : tone === 'amber' ? 'bg-amber-500/12 text-amber-200 ring-amber-400/30'
    : tone === 'purple' ? 'bg-purple-500/12 text-purple-200 ring-purple-400/30'
    : 'bg-elevated/60 text-text-2 ring-border'
  return (
    <span className={`inline-flex items-center gap-1.5 h-7 px-2.5 rounded-full ring-1 text-[11.5px] tracking-tight ${palette}`}>
      <span className="font-semibold tabular-nums">{value}</span>
      <span className="opacity-80">{label}</span>
    </span>
  )
}

function StateChip({ state }: { state: AggregatedRequest['state'] }) {
  const Icon =
    state === 'processing' ? RefreshCw
    : state === 'partial' ? Check
    : state === 'pending' ? Clock
    : AlertCircle
  const tone =
    state === 'processing' ? 'bg-blue-500/20 text-blue-200 ring-blue-400/30'
    : state === 'partial' ? 'bg-amber-500/20 text-amber-200 ring-amber-400/30'
    : state === 'pending' ? 'bg-purple-500/20 text-purple-200 ring-purple-400/30'
    : 'bg-elevated/80 text-text-2 ring-border'
  const label =
    state === 'processing' ? 'Downloading'
    : state === 'partial' ? 'Partial'
    : state === 'pending' ? 'Pending'
    : 'Requested'
  return (
    <span className={`inline-flex items-center gap-1 h-[16px] px-1.5 rounded text-[9.5px] font-bold uppercase tracking-[0.06em] ring-1 ${tone}`}>
      <Icon size={9} stroke={2.5} />
      {label}
    </span>
  )
}
