import { useEffect, useMemo, useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { useQueryClient } from '@tanstack/react-query'
import { useNavigate } from 'react-router-dom'
import { X, Database, Check, AlertCircle, Settings } from '../../lib/icons'
import { useArrInstances, type ArrInstance } from '../../stores/arr-instances-store'
import { useArrProfiles, useArrRootFolders, useSonarrLanguageProfiles } from '../../hooks/use-arr'
import { useOverrideRules, applyOverrideRules } from '../../stores/override-rules-store'
import { radarrClient } from '../../api/radarr'
import { sonarrClient, type SonarrSeason } from '../../api/sonarr'
import Select, { type SelectOption } from '../ui/Select'
import type { TmdbMovie, TmdbTvShow } from '../../api/tmdb'

/** Merged shape that lets call sites access both movie and TV fields without
 *  rediscriminating the union at every read. */
type TmdbDetail = Partial<TmdbMovie> & Partial<TmdbTvShow>

interface Props {
  open: boolean
  onClose: () => void
  /** TMDB id of the item to request. */
  tmdbId: number
  kind: 'movie' | 'tv'
  /** TMDB metadata so we can pre-fill title / year / images for *arr. */
  tmdbData: TmdbMovie | TmdbTvShow | null
}

/**
 * One-shot request flow. Opens with a default tier (regular) and lets
 * the user choose between regular/4K, quality profile, root folder,
 * monitored, search-on-add, and per-season selection for shows. On
 * submit we call Radarr's POST /movie or Sonarr's POST /series.
 *
 * Override rules (Wave 31) hook in here later: when the user opens
 * the modal, we evaluate active rules against the TMDB metadata and
 * pre-select tier + profile + root folder accordingly.
 */
export default function RequestModal({ open, onClose, tmdbId, kind, tmdbData }: Props) {
  const qc = useQueryClient()
  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 tierOptions: Array<{ tier: 'default' | '4k'; instance: ArrInstance | null }> = kind === 'movie'
    ? [
        { tier: 'default', instance: radarr },
        { tier: '4k', instance: radarr4k },
      ]
    : [
        { tier: 'default', instance: sonarr },
        { tier: '4k', instance: sonarr4k },
      ]

  // Evaluate override rules once per modal open so the tier and other
  // selections start from the rule-driven values when one matches.
  const rules = useOverrideRules(s => s.rules)
  const matched = useMemo(() => {
    if (!tmdbData) return null
    const d = tmdbData as TmdbDetail
    const ctx = {
      kind,
      genres: (d.genres || []).map(g => g.name).filter(Boolean) as string[],
      originalLanguage: d.original_language || null,
      year: parseInt(
        String(d.release_date || d.first_air_date || '').slice(0, 4),
      ) || null,
      keywordIds: (d.keywords?.keywords || d.keywords?.results || [])
        .map(k => k?.id)
        .filter((x): x is number => typeof x === 'number'),
    }
    return applyOverrideRules(rules, ctx)
  }, [rules, tmdbData, kind])

  const [tier, setTier] = useState<'default' | '4k'>(matched?.tier || 'default')
  const active = tierOptions.find(t => t.tier === tier)?.instance || null

  const profiles = useArrProfiles(active)
  const rootFolders = useArrRootFolders(active)
  const langProfiles = useSonarrLanguageProfiles(active)

  const [profileId, setProfileId] = useState<number | null>(null)
  const [rootFolder, setRootFolder] = useState<string | null>(null)
  const [langProfileId, setLangProfileId] = useState<number | null>(null)
  const [monitored, setMonitored] = useState(true)
  const [searchOnAdd, setSearchOnAdd] = useState(true)
  const [seasonsRequested, setSeasonsRequested] = useState<Record<number, boolean>>({})
  const [submitting, setSubmitting] = useState(false)
  const [result, setResult] = useState<{ ok: boolean; message: string } | null>(null)

  // Initialise selections from the active instance's defaults whenever
  // the user picks a different tier (or opens the modal). When a rule
  // matched, its overrides take precedence over the instance defaults.
  useEffect(() => {
    if (!active) return
    setProfileId(matched?.qualityProfileId ?? active.defaultQualityProfileId ?? null)
    setRootFolder(matched?.rootFolderPath ?? active.defaultRootFolder ?? null)
    setLangProfileId(matched?.languageProfileId ?? active.defaultLanguageProfileId ?? null)
    setMonitored(active.defaultMonitored ?? true)
  }, [active, matched])

  // For shows, pre-populate the seasons-to-request map from the TMDB
  // payload. All seasons selected by default; user can opt out per-season.
  const tvSeasons = useMemo(() => {
    if (kind !== 'tv' || !tmdbData) return []
    const seasons = (tmdbData as TmdbTvShow).seasons || []
    return seasons.filter(s => (s.season_number ?? 0) > 0 || s.episode_count > 0)
  }, [kind, tmdbData])

  useEffect(() => {
    if (kind !== 'tv') return
    const init: Record<number, boolean> = {}
    for (const s of tvSeasons) init[s.season_number ?? 0] = true
    setSeasonsRequested(init)
  }, [kind, tvSeasons])

  // Esc closes.
  useEffect(() => {
    if (!open) return
    function onKey(e: KeyboardEvent) {
      if (e.key === 'Escape') onClose()
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [open, onClose])

  // Reset transient state when the modal closes so re-opening it for a
  // different item starts fresh.
  useEffect(() => {
    if (!open) {
      setResult(null)
      setSubmitting(false)
    }
  }, [open])

  // Auto-close after a successful request - the user gets a brief flash
  // of confirmation, then we get out of their way.
  useEffect(() => {
    if (result?.ok) {
      const id = setTimeout(() => onClose(), 1800)
      return () => clearTimeout(id)
    }
  }, [result?.ok, onClose])

  async function submit() {
    if (!active || !profileId || !rootFolder) return
    if (!tmdbData) return
    setSubmitting(true)
    setResult(null)
    try {
      if (kind === 'movie' && active.kind === 'radarr') {
        const lookup = await radarrClient(active).lookupByTmdbId(tmdbId)
        const match = lookup?.[0]
        if (!match) {
          setResult({ ok: false, message: "Radarr couldn't find this movie on TMDB - check the id." })
          return
        }
        const added = await radarrClient(active).addMovie({
          tmdbId,
          qualityProfileId: profileId,
          rootFolderPath: rootFolder,
          monitored,
          searchOnAdd,
          title: match.title,
          titleSlug: (match.title || '').toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''),
          year: match.year || 0,
          images: match.images,
          minimumAvailability: 'released',
        })
        if (added) {
          setResult({ ok: true, message: `Requested "${match.title}". Radarr will start searching now.` })
          qc.invalidateQueries({ queryKey: ['radarr', 'library'] })
          qc.invalidateQueries({ queryKey: ['radarr', 'queue'] })
        } else {
          setResult({ ok: false, message: 'Radarr rejected the request. It may already exist there.' })
        }
      } else if (kind === 'tv' && active.kind === 'sonarr') {
        const lookup = await sonarrClient(active).lookupByTmdbId(tmdbId)
        const match = lookup?.[0]
        if (!match || !match.tvdbId) {
          setResult({ ok: false, message: "Sonarr couldn't find a TVDB id for this show. Check that Sonarr's metadata is populated." })
          return
        }
        const seasons: SonarrSeason[] = (match.seasons || []).map(s => ({
          seasonNumber: s.seasonNumber,
          monitored: !!seasonsRequested[s.seasonNumber],
        }))
        const added = await sonarrClient(active).addSeries({
          tvdbId: match.tvdbId,
          qualityProfileId: profileId,
          languageProfileId: langProfileId ?? undefined,
          rootFolderPath: rootFolder,
          monitored,
          searchOnAdd,
          title: match.title,
          titleSlug: match.titleSlug || '',
          year: match.year || 0,
          images: match.images,
          seasons,
        })
        if (added) {
          setResult({ ok: true, message: `Requested "${match.title}". Sonarr is monitoring the seasons you picked.` })
          qc.invalidateQueries({ queryKey: ['sonarr', 'library'] })
          qc.invalidateQueries({ queryKey: ['sonarr', 'queue'] })
        } else {
          setResult({ ok: false, message: 'Sonarr rejected the request. It may already exist there.' })
        }
      } else {
        setResult({ ok: false, message: `No ${kind === 'movie' ? 'Radarr' : 'Sonarr'} instance configured for this tier.` })
      }
    } catch (e: any) {
      setResult({ ok: false, message: String(e?.message || e) })
    } finally {
      setSubmitting(false)
    }
  }

  const noInstance = !radarr && !radarr4k && !sonarr && !sonarr4k
  const noTierForKind = kind === 'movie'
    ? !radarr && !radarr4k
    : !sonarr && !sonarr4k

  return (
    <AnimatePresence>
      {open && (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.18 }}
          onClick={onClose}
          className="fixed inset-0 z-[80] grid place-items-center bg-black/65 backdrop-blur-sm p-6"
          role="dialog"
        >
          <motion.div
            initial={{ y: 20, scale: 0.96 }}
            animate={{ y: 0, scale: 1 }}
            exit={{ y: 20, scale: 0.96 }}
            transition={{ duration: 0.32, ease: [0.16, 1, 0.3, 1] }}
            onClick={e => e.stopPropagation()}
            className="relative w-full max-w-xl rounded-2xl bg-surface ring-1 ring-border-strong shadow-[0_30px_80px_-20px_rgba(0,0,0,0.85)] max-h-[88vh] overflow-y-auto"
          >
            <header className="sticky top-0 z-10 flex items-center justify-between p-5 pb-3 bg-surface/95 backdrop-blur border-b border-border">
              <div className="flex items-center gap-2">
                <Database size={16} className="text-accent" />
                <h2 className="text-[16px] font-semibold tracking-tight text-text-1">
                  Request {kind === 'movie' ? 'movie' : 'show'}
                </h2>
              </div>
              <button
                onClick={onClose}
                aria-label="Close"
                className="w-8 h-8 grid place-items-center rounded-full text-text-3 hover:text-text-1 hover:bg-elevated transition focus-ring"
              >
                <X size={14} stroke={2} />
              </button>
            </header>

            <div className="p-5 space-y-4 text-[13px]">
              {tmdbData && (() => { const d = tmdbData as TmdbDetail; return (
                <p className="text-[14px] text-text-1 font-semibold tracking-tight">
                  {d.title || d.name}
                  {d.release_date && (
                    <span className="text-text-3 font-normal ml-2 tabular-nums">
                      {String(d.release_date).slice(0, 4)}
                    </span>
                  )}
                  {d.first_air_date && (
                    <span className="text-text-3 font-normal ml-2 tabular-nums">
                      {String(d.first_air_date).slice(0, 4)}
                    </span>
                  )}
                </p>
              )})()}

              {noInstance || noTierForKind ? (
                <div className="rounded-xl bg-amber-500/8 ring-1 ring-amber-400/25 p-4 flex items-start gap-3">
                  <AlertCircle size={16} className="text-amber-300 shrink-0 mt-0.5" />
                  <div className="min-w-0 flex-1">
                    <p className="text-[12.5px] text-amber-100 font-medium mb-1">
                      {noInstance
                        ? 'No Sonarr or Radarr instance configured'
                        : `No ${kind === 'movie' ? 'Radarr' : 'Sonarr'} instance configured`}
                    </p>
                    <p className="text-[11.5px] text-amber-100/80 leading-relaxed mb-2.5">
                      {noInstance
                        ? 'Connect a Sonarr or Radarr instance to start requesting new titles.'
                        : `Add a ${kind === 'movie' ? 'Radarr' : 'Sonarr'} instance to request ${kind === 'movie' ? 'movies' : 'shows'}.`}
                    </p>
                    <button
                      onClick={() => {
                        onClose()
                        navigate('/settings#sonarr-radarr')
                      }}
                      className="inline-flex items-center gap-1.5 h-8 px-3 rounded-full bg-amber-500/15 ring-1 ring-amber-400/40 text-[11.5px] text-amber-100 font-semibold tracking-tight hover:bg-amber-500/25 transition focus-ring"
                    >
                      <Settings size={11} stroke={2} />
                      Open Settings
                    </button>
                  </div>
                </div>
              ) : (
                <>
                  {/* Tier picker */}
                  {tierOptions.filter(t => t.instance).length > 1 && (
                    <Field label="Quality tier">
                      <div className="inline-flex bg-elevated/40 ring-1 ring-border rounded-md p-0.5">
                        {tierOptions.map(t => {
                          if (!t.instance) return null
                          const on = tier === t.tier
                          return (
                            <button
                              key={t.tier}
                              onClick={() => setTier(t.tier)}
                              className={`h-8 px-3 rounded text-[11.5px] font-medium tracking-tight transition focus-ring ${
                                on ? 'bg-accent text-void' : 'text-text-3 hover:text-text-1'
                              }`}
                            >
                              {t.tier === '4k' ? '4K' : 'Regular'}
                              <span className="ml-1.5 text-[10px] opacity-70">{t.instance.name}</span>
                            </button>
                          )
                        })}
                      </div>
                    </Field>
                  )}

                  <Field label="Quality profile">
                    <Select
                      ariaLabel="Quality profile"
                      width="w-full"
                      value={profileId != null ? String(profileId) : ''}
                      onChange={v => setProfileId(v ? Number(v) : null)}
                      placeholder="Pick a profile"
                      options={(profiles.data || []).map<SelectOption<string>>((p: any) => ({
                        value: String(p.id),
                        label: p.name,
                      }))}
                    />
                  </Field>

                  <Field label="Root folder">
                    <Select
                      ariaLabel="Root folder"
                      width="w-full"
                      value={rootFolder ?? ''}
                      onChange={v => setRootFolder(v || null)}
                      placeholder="Pick a folder"
                      options={(rootFolders.data || []).map<SelectOption<string>>((r: any) => ({
                        value: r.path,
                        label: <span className="font-mono">{r.path}</span>,
                      }))}
                    />
                  </Field>

                  {kind === 'tv' && langProfiles.data && langProfiles.data.length > 0 && (
                    <Field label="Language profile">
                      <Select
                        ariaLabel="Language profile"
                        width="w-full"
                        value={langProfileId != null ? String(langProfileId) : ''}
                        onChange={v => setLangProfileId(v ? Number(v) : null)}
                        placeholder="Pick a language profile"
                        options={(langProfiles.data || []).map<SelectOption<string>>((p: any) => ({
                          value: String(p.id),
                          label: p.name,
                        }))}
                      />
                    </Field>
                  )}

                  {kind === 'tv' && tvSeasons.length > 0 && (
                    <div>
                      <div className="flex items-center justify-between mb-1.5">
                        <label className="block text-[10.5px] uppercase tracking-[0.16em] font-semibold text-text-3">
                          Seasons
                        </label>
                        <div className="flex items-center gap-2 text-[11px]">
                          <span className="text-text-4 tabular-nums">
                            <span className="text-text-2 font-medium">{Object.values(seasonsRequested).filter(Boolean).length}</span>
                            {' / '}
                            {tvSeasons.length} selected
                          </span>
                          <button
                            type="button"
                            onClick={() => {
                              const next: Record<number, boolean> = {}
                              for (const s of tvSeasons) next[s.season_number ?? 0] = true
                              setSeasonsRequested(next)
                            }}
                            className="text-accent hover:text-accent-hover transition focus-ring rounded font-medium tracking-tight"
                          >
                            All
                          </button>
                          <span className="text-text-5">·</span>
                          <button
                            type="button"
                            onClick={() => setSeasonsRequested({})}
                            className="text-text-3 hover:text-text-1 transition focus-ring rounded font-medium tracking-tight"
                          >
                            None
                          </button>
                        </div>
                      </div>
                      <div className="flex flex-wrap gap-1.5 max-h-44 overflow-y-auto py-1 px-0.5 -mx-0.5 hide-scrollbar">
                        {tvSeasons.map(s => {
                          const num = s.season_number ?? 0
                          const on = !!seasonsRequested[num]
                          const label = num === 0 ? 'Specials' : `Season ${num}`
                          return (
                            <button
                              key={num}
                              onClick={() =>
                                setSeasonsRequested(p => ({ ...p, [num]: !on }))
                              }
                              className={`h-7 px-2.5 rounded-full text-[11px] font-medium tracking-tight transition border focus-ring ${
                                on
                                  ? 'bg-accent/15 text-accent border-accent/40'
                                  : 'bg-elevated/40 text-text-2 border-border hover:border-border-strong'
                              }`}
                            >
                              {label}
                              <span className={`tabular-nums ml-1 ${on ? 'text-accent/70' : 'text-text-4'}`}>{s.episode_count || 0}</span>
                            </button>
                          )
                        })}
                      </div>
                    </div>
                  )}

                  <div className="flex items-center gap-4 pt-1 flex-wrap">
                    <label className="inline-flex items-center gap-2 text-[12px] text-text-2 cursor-pointer">
                      <input
                        type="checkbox"
                        checked={monitored}
                        onChange={e => setMonitored(e.target.checked)}
                        className="accent-amber-500"
                      />
                      Monitor for new episodes / releases
                    </label>
                    <label className="inline-flex items-center gap-2 text-[12px] text-text-2 cursor-pointer">
                      <input
                        type="checkbox"
                        checked={searchOnAdd}
                        onChange={e => setSearchOnAdd(e.target.checked)}
                        className="accent-amber-500"
                      />
                      Search now
                    </label>
                  </div>

                  {result && (
                    <div
                      className={`rounded-md p-3 text-[12px] leading-relaxed ring-1 ${
                        result.ok
                          ? 'bg-success/10 text-success ring-success/30'
                          : 'bg-red-500/10 text-red-200 ring-red-500/30'
                      }`}
                    >
                      <span className="inline-flex items-center gap-1.5 font-medium">
                        {result.ok ? <Check size={12} stroke={2.5} /> : <AlertCircle size={12} />}
                        {result.message}
                      </span>
                    </div>
                  )}
                </>
              )}
            </div>

            <footer className="sticky bottom-0 z-10 flex items-center justify-end gap-2 p-5 pt-3 bg-surface/95 backdrop-blur border-t border-border">
              <button
                onClick={onClose}
                className="h-10 px-4 rounded-full text-[12.5px] text-text-2 hover:text-text-1 hover:bg-elevated transition focus-ring"
              >
                {result?.ok ? 'Done' : 'Cancel'}
              </button>
              {!result?.ok && (
                <button
                  onClick={submit}
                  disabled={submitting || noInstance || noTierForKind || !profileId || !rootFolder}
                  className="h-10 px-5 rounded-full bg-accent text-void text-[12.5px] font-semibold tracking-tight transition disabled:opacity-40 disabled:cursor-not-allowed hover:bg-accent-hover focus-ring"
                >
                  {submitting ? 'Submitting...' : 'Request'}
                </button>
              )}
            </footer>
          </motion.div>
        </motion.div>
      )}
    </AnimatePresence>
  )
}

function Field({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div>
      <label className="block text-[10.5px] uppercase tracking-[0.16em] font-semibold text-text-3 mb-1.5">
        {label}
      </label>
      {children}
    </div>
  )
}
