import { useMemo, useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import {
  Filter,
  Tv2,
  Film as FilmIcon,
  RefreshCw,
  Star,
  Clock,
  Calendar,
  Languages,
  Activity,
  Boxes,
  ChevronUp,
  Library,
} from '../../lib/icons'
import { TMDB_MOVIE_GENRES, TMDB_TV_GENRES } from '../../lib/tmdb-genres'
import { Chip } from '../ui/Chip'
import Select, { type SelectOption } from '../ui/Select'

export interface DiscoverFilterState {
  kind: 'movie' | 'tv'
  sortBy: string
  genres: number[]
  yearFrom: number | null
  yearTo: number | null
  voteAverageGte: number | null
  voteCountGte: number | null
  runtimeGte: number | null
  runtimeLte: number | null
  language: string | null
  watchProviders: number[]
  watchRegion: string | null
  status: string | null
  hideOwned: boolean
}

export const DEFAULT_FILTERS: DiscoverFilterState = {
  kind: 'movie',
  sortBy: 'popularity.desc',
  genres: [],
  yearFrom: null,
  yearTo: null,
  voteAverageGte: null,
  voteCountGte: null,
  runtimeGte: null,
  runtimeLte: null,
  language: null,
  watchProviders: [],
  watchRegion: null,
  status: null,
  hideOwned: true,
}

const MOVIE_SORTS: SelectOption[] = [
  { value: 'popularity.desc', label: 'Popularity (high to low)' },
  { value: 'popularity.asc', label: 'Popularity (low to high)' },
  { value: 'vote_average.desc', label: 'Rating (high to low)' },
  { value: 'vote_average.asc', label: 'Rating (low to high)' },
  { value: 'primary_release_date.desc', label: 'Release date (newest)' },
  { value: 'primary_release_date.asc', label: 'Release date (oldest)' },
  { value: 'revenue.desc', label: 'Revenue (high to low)' },
  { value: 'original_title.asc', label: 'Title (A-Z)' },
  { value: 'original_title.desc', label: 'Title (Z-A)' },
]

const TV_SORTS: SelectOption[] = [
  { value: 'popularity.desc', label: 'Popularity (high to low)' },
  { value: 'popularity.asc', label: 'Popularity (low to high)' },
  { value: 'vote_average.desc', label: 'Rating (high to low)' },
  { value: 'vote_average.asc', label: 'Rating (low to high)' },
  { value: 'first_air_date.desc', label: 'First air date (newest)' },
  { value: 'first_air_date.asc', label: 'First air date (oldest)' },
  { value: 'name.asc', label: 'Title (A-Z)' },
  { value: 'name.desc', label: 'Title (Z-A)' },
]

// Radix Select rejects empty-string values, so the "any" sentinel is a
// magic string we translate to null at the API boundary.
const ANY = '__any__'

const TV_STATUS_OPTIONS: SelectOption[] = [
  { value: ANY, label: 'Any status' },
  { value: '0', label: 'Returning Series' },
  { value: '1', label: 'Planned' },
  { value: '2', label: 'In Production' },
  { value: '3', label: 'Ended' },
  { value: '4', label: 'Canceled' },
  { value: '5', label: 'Pilot' },
]

const LANGUAGES: SelectOption[] = [
  { value: ANY, label: 'Any language' },
  { value: 'en', label: 'English' },
  { value: 'ja', label: 'Japanese' },
  { value: 'ko', label: 'Korean' },
  { value: 'fr', label: 'French' },
  { value: 'es', label: 'Spanish' },
  { value: 'de', label: 'German' },
  { value: 'it', label: 'Italian' },
  { value: 'zh', label: 'Chinese' },
  { value: 'hi', label: 'Hindi' },
  { value: 'pt', label: 'Portuguese' },
  { value: 'ru', label: 'Russian' },
  { value: 'sv', label: 'Swedish' },
  { value: 'da', label: 'Danish' },
  { value: 'nl', label: 'Dutch' },
  { value: 'pl', label: 'Polish' },
  { value: 'tr', label: 'Turkish' },
  { value: 'ar', label: 'Arabic' },
  { value: 'th', label: 'Thai' },
]

const VOTE_COUNT_OPTIONS: SelectOption[] = [
  { value: '0', label: 'Any number of votes' },
  { value: '50', label: 'At least 50 votes' },
  { value: '200', label: 'At least 200 votes' },
  { value: '500', label: 'At least 500 votes' },
  { value: '1000', label: 'At least 1,000 votes' },
  { value: '5000', label: 'At least 5,000 votes' },
  { value: '10000', label: 'At least 10,000 votes' },
]

const COMMON_PROVIDERS: Array<{ id: number; label: string }> = [
  { id: 8, label: 'Netflix' },
  { id: 9, label: 'Prime Video' },
  { id: 337, label: 'Disney+' },
  { id: 1899, label: 'Max' },
  { id: 350, label: 'Apple TV+' },
  { id: 15, label: 'Hulu' },
  { id: 531, label: 'Paramount+' },
  { id: 386, label: 'Peacock' },
  { id: 283, label: 'Crunchyroll' },
  { id: 2, label: 'Apple TV' },
  { id: 192, label: 'YouTube' },
  { id: 3, label: 'Google Play' },
  { id: 68, label: 'Microsoft' },
]

export function countActiveFilters(f: DiscoverFilterState): number {
  let n = 0
  if (f.genres.length > 0) n++
  if (f.yearFrom != null || f.yearTo != null) n++
  if (f.voteAverageGte != null) n++
  if (f.voteCountGte != null) n++
  if (f.runtimeGte != null || f.runtimeLte != null) n++
  if (f.language) n++
  if (f.watchProviders.length > 0) n++
  if (f.status) n++
  if (f.sortBy !== DEFAULT_FILTERS.sortBy) n++
  return n
}

export function hasAnyActiveFilters(f: DiscoverFilterState): boolean {
  return countActiveFilters(f) > 0
}

interface Props {
  filters: DiscoverFilterState
  onChange: (next: DiscoverFilterState) => void
  region: string
}

/* ────────────────────────────────────────────────────────────── */
/*  Top toolbar - kind toggle + sort + filter button + hide-owned */
/* ────────────────────────────────────────────────────────────── */

export default function DiscoverFilters({ filters, onChange, region }: Props) {
  const [panelOpen, setPanelOpen] = useState(false)
  const activeCount = countActiveFilters(filters)
  const sorts = filters.kind === 'movie' ? MOVIE_SORTS : TV_SORTS

  const set = <K extends keyof DiscoverFilterState>(k: K, v: DiscoverFilterState[K]) => {
    onChange({ ...filters, [k]: v })
  }

  return (
    <div className="px-7">
      {/* Toolbar */}
      <div className="flex items-center gap-2 flex-wrap">
        <KindToggle
          value={filters.kind}
          onChange={k => onChange({ ...filters, kind: k, status: null, runtimeGte: null, runtimeLte: null })}
        />

        <div className="h-6 w-px bg-border mx-1" />

        <Select
          ariaLabel="Sort by"
          size="sm"
          width="min-w-[220px]"
          value={filters.sortBy}
          onChange={v => set('sortBy', v)}
          options={sorts}
        />

        <Chip
          as="button"
          size="md"
          tone={activeCount > 0 ? 'accent' : 'outline'}
          icon={<Filter size={12} stroke={2} />}
          trailing={activeCount > 0 ? (
            <span className="inline-flex items-center justify-center min-w-[16px] h-[16px] px-1 rounded-full bg-accent text-void text-[10px] font-bold tabular-nums">
              {activeCount}
            </span>
          ) : null}
          onClick={() => setPanelOpen(o => !o)}
        >
          Filters
        </Chip>

        <Chip
          as="button"
          size="md"
          tone={filters.hideOwned ? 'accent' : 'outline'}
          icon={<Library size={12} stroke={2} />}
          onClick={() => set('hideOwned', !filters.hideOwned)}
        >
          Hide items in your library
        </Chip>

        {activeCount > 0 && (
          <button
            onClick={() => onChange({
              ...DEFAULT_FILTERS,
              kind: filters.kind,
              hideOwned: filters.hideOwned,
            })}
            className="inline-flex items-center gap-1.5 h-7 px-2.5 rounded-md text-[11.5px] tracking-tight text-text-3 hover:text-text-1 transition focus-ring"
          >
            <RefreshCw size={11} stroke={2} />
            Reset
          </button>
        )}
      </div>

      {/* Filter panel */}
      <AnimatePresence>
        {panelOpen && (
          <motion.div
            initial={{ opacity: 0, height: 0 }}
            animate={{ opacity: 1, height: 'auto' }}
            exit={{ opacity: 0, height: 0 }}
            transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}
            className="overflow-hidden"
          >
            <div className="mt-4 p-6 rounded-xl bg-elevated/40 border border-border">
              <FilterPanel filters={filters} onChange={onChange} region={region} />
              <div className="mt-5 pt-4 border-t border-border/60 flex justify-end">
                <button
                  onClick={() => setPanelOpen(false)}
                  className="inline-flex items-center gap-1.5 h-8 px-3 rounded-md text-[11.5px] tracking-tight text-text-3 hover:text-text-1 transition focus-ring"
                >
                  <ChevronUp size={11} stroke={2} />
                  Collapse
                </button>
              </div>
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  )
}

function KindToggle({
  value,
  onChange,
}: {
  value: 'movie' | 'tv'
  onChange: (k: 'movie' | 'tv') => void
}) {
  return (
    <div className="flex p-0.5 bg-void/60 rounded-md border border-border">
      {([
        { v: 'movie' as const, label: 'Movies', Icon: FilmIcon },
        { v: 'tv' as const, label: 'TV', Icon: Tv2 },
      ]).map(opt => {
        const isActive = value === opt.v
        return (
          <button
            key={opt.v}
            onClick={() => onChange(opt.v)}
            className={`relative h-7 px-3 text-[11.5px] font-medium tracking-tight transition-colors duration-150 rounded focus-ring inline-flex items-center gap-1.5 ${
              isActive ? 'text-void' : 'text-text-3 hover:text-text-1'
            }`}
          >
            {isActive && (
              <motion.span
                layoutId="discover-kind-active"
                className="absolute inset-0 bg-accent rounded"
                transition={{ type: 'spring', stiffness: 380, damping: 30 }}
              />
            )}
            <span className="relative flex items-center gap-1.5">
              <opt.Icon size={12} stroke={2.25} />
              {opt.label}
            </span>
          </button>
        )
      })}
    </div>
  )
}

/* ────────────────────────────────────────────────────────────── */
/*  Expandable filter panel                                        */
/* ────────────────────────────────────────────────────────────── */

function FilterPanel({ filters, onChange, region }: Props) {
  const set = <K extends keyof DiscoverFilterState>(k: K, v: DiscoverFilterState[K]) => {
    onChange({ ...filters, [k]: v })
  }

  const genres = useMemo(() => {
    const map = filters.kind === 'movie' ? TMDB_MOVIE_GENRES : TMDB_TV_GENRES
    const seen = new Set<number>()
    return Object.entries(map).flatMap(([label, id]) => {
      if (seen.has(id)) return []
      seen.add(id)
      return [{ label, id }]
    })
  }, [filters.kind])

  function toggleGenre(id: number) {
    const next = filters.genres.includes(id)
      ? filters.genres.filter(g => g !== id)
      : [...filters.genres, id]
    set('genres', next)
  }

  function toggleProvider(id: number) {
    const next = filters.watchProviders.includes(id)
      ? filters.watchProviders.filter(p => p !== id)
      : [...filters.watchProviders, id]
    set('watchProviders', next)
    if (next.length > 0 && !filters.watchRegion) {
      set('watchRegion', region)
    }
  }

  return (
    <div className="space-y-7">
      {/* Genres - always full row, can be many */}
      <Field
        label="Genres"
        icon={<Boxes size={11} stroke={2} />}
        value={filters.genres.length > 0 ? `${filters.genres.length} selected` : 'Any'}
      >
        <div className="flex flex-wrap gap-1.5">
          {genres.map(g => (
            <Chip
              as="button"
              key={g.id}
              size="sm"
              tone={filters.genres.includes(g.id) ? 'accent' : 'outline'}
              onClick={() => toggleGenre(g.id)}
            >
              {g.label}
            </Chip>
          ))}
        </div>
      </Field>

      {/* Streaming providers - always full row */}
      <Field
        label="Streaming services"
        icon={<Tv2 size={11} stroke={2} />}
        value={
          filters.watchProviders.length > 0
            ? `${filters.watchProviders.length} in ${filters.watchRegion || region}`
            : 'Any'
        }
      >
        <div className="flex flex-wrap gap-1.5">
          {COMMON_PROVIDERS.map(p => (
            <Chip
              as="button"
              key={p.id}
              size="sm"
              tone={filters.watchProviders.includes(p.id) ? 'accent' : 'outline'}
              onClick={() => toggleProvider(p.id)}
            >
              {p.label}
            </Chip>
          ))}
        </div>
      </Field>

      {/* Two-column grid for the smaller fields */}
      <div className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-7">
        <Field
          label={filters.kind === 'movie' ? 'Release year' : 'First air year'}
          icon={<Calendar size={11} stroke={2} />}
          value={
            filters.yearFrom || filters.yearTo
              ? `${filters.yearFrom ?? '...'} to ${filters.yearTo ?? '...'}`
              : 'Any year'
          }
        >
          <div className="flex items-center gap-2">
            <YearInput
              value={filters.yearFrom}
              onChange={v => set('yearFrom', v)}
              placeholder="From"
            />
            <span className="text-text-4 text-[12px]">to</span>
            <YearInput
              value={filters.yearTo}
              onChange={v => set('yearTo', v)}
              placeholder="To"
            />
          </div>
        </Field>

        <Field
          label="Minimum rating"
          icon={<Star size={11} stroke={2} />}
          value={filters.voteAverageGte != null ? `${filters.voteAverageGte} / 10` : 'Any'}
        >
          <div className="flex items-center gap-3">
            <input
              type="range"
              min={0}
              max={10}
              step={0.5}
              value={filters.voteAverageGte ?? 0}
              onChange={e => set('voteAverageGte', Number(e.target.value) === 0 ? null : Number(e.target.value))}
              aria-label="Minimum rating"
              className="slider flex-1"
            />
            <span className="inline-flex items-center justify-center min-w-[44px] h-7 px-2 rounded-md bg-void/60 border border-border text-[11.5px] text-text-1 font-medium tabular-nums">
              {filters.voteAverageGte ?? 0}
            </span>
          </div>
        </Field>

        <Field
          label="Vote count"
          icon={<Activity size={11} stroke={2} />}
          value={
            filters.voteCountGte != null
              ? `${filters.voteCountGte.toLocaleString()}+`
              : 'Any'
          }
        >
          <Select
            ariaLabel="Minimum vote count"
            size="sm"
            value={String(filters.voteCountGte ?? 0)}
            onChange={v => set('voteCountGte', Number(v) === 0 ? null : Number(v))}
            options={VOTE_COUNT_OPTIONS}
            width="w-full"
          />
        </Field>

        <Field
          label="Original language"
          icon={<Languages size={11} stroke={2} />}
          value={LANGUAGES.find(l => l.value === filters.language)?.label as string || 'Any'}
        >
          <Select
            ariaLabel="Original language"
            size="sm"
            value={filters.language ?? ANY}
            onChange={v => set('language', v === ANY ? null : v)}
            options={LANGUAGES}
            width="w-full"
          />
        </Field>

        {filters.kind === 'movie' && (
          <Field
            label="Runtime"
            icon={<Clock size={11} stroke={2} />}
            value={`${filters.runtimeGte ?? 0} - ${filters.runtimeLte ?? 240}m`}
          >
            <div className="flex items-center gap-3">
              <span className="text-[10.5px] text-text-4 tabular-nums w-8 text-right">
                {filters.runtimeGte ?? 0}m
              </span>
              <input
                type="range"
                min={0}
                max={240}
                step={10}
                value={filters.runtimeGte ?? 0}
                onChange={e => set('runtimeGte', Number(e.target.value) === 0 ? null : Number(e.target.value))}
                aria-label="Minimum runtime"
                className="slider flex-1"
              />
              <input
                type="range"
                min={0}
                max={240}
                step={10}
                value={filters.runtimeLte ?? 240}
                onChange={e => set('runtimeLte', Number(e.target.value) === 240 ? null : Number(e.target.value))}
                aria-label="Maximum runtime"
                className="slider flex-1"
              />
              <span className="text-[10.5px] text-text-4 tabular-nums w-10">
                {filters.runtimeLte ?? 240}m
              </span>
            </div>
          </Field>
        )}

        {filters.kind === 'tv' && (
          <Field
            label="Series status"
            icon={<Activity size={11} stroke={2} />}
            value={TV_STATUS_OPTIONS.find(o => o.value === filters.status)?.label as string || 'Any'}
          >
            <Select
              ariaLabel="Series status"
              size="sm"
              value={filters.status ?? ANY}
              onChange={v => set('status', v === ANY ? null : v)}
              options={TV_STATUS_OPTIONS}
              width="w-full"
            />
          </Field>
        )}
      </div>
    </div>
  )
}

function Field({
  label,
  icon,
  value,
  children,
}: {
  label: string
  icon?: React.ReactNode
  value?: string
  children: React.ReactNode
}) {
  return (
    <div>
      <div className="flex items-baseline justify-between gap-3 mb-2.5">
        <p className="text-[10.5px] font-semibold text-text-2 uppercase tracking-[0.16em] inline-flex items-center gap-1.5">
          {icon && <span className="text-text-3">{icon}</span>}
          {label}
        </p>
        {value && (
          <p className="text-[11px] text-text-4 tracking-tight tabular-nums truncate max-w-[60%]">
            {value}
          </p>
        )}
      </div>
      {children}
    </div>
  )
}

function YearInput({
  value,
  onChange,
  placeholder,
}: {
  value: number | null
  onChange: (v: number | null) => void
  placeholder: string
}) {
  return (
    <input
      type="number"
      min="1900"
      max="2100"
      value={value ?? ''}
      onChange={e => {
        const n = Number(e.target.value)
        onChange(e.target.value === '' || Number.isNaN(n) ? null : n)
      }}
      placeholder={placeholder}
      className="flex-1 h-9 px-3 rounded-md bg-void/50 hover:bg-void/70 ring-1 ring-border focus:ring-accent/50 focus:ring-2 outline-none text-[12.5px] tabular-nums text-text-1 placeholder:text-text-4 transition-all duration-150 font-mono"
    />
  )
}

/** Convert filter state to TMDB discover query params. */
export function filtersToTmdbParams(f: DiscoverFilterState): Record<string, string> {
  const p: Record<string, string> = { sort_by: f.sortBy }
  if (f.genres.length > 0) p.with_genres = f.genres.join(',')
  if (f.yearFrom != null) {
    p[f.kind === 'movie' ? 'primary_release_date.gte' : 'first_air_date.gte'] = `${f.yearFrom}-01-01`
  }
  if (f.yearTo != null) {
    p[f.kind === 'movie' ? 'primary_release_date.lte' : 'first_air_date.lte'] = `${f.yearTo}-12-31`
  }
  if (f.voteAverageGte != null) p['vote_average.gte'] = String(f.voteAverageGte)
  if (f.voteCountGte != null) p['vote_count.gte'] = String(f.voteCountGte)
  if (f.runtimeGte != null) p['with_runtime.gte'] = String(f.runtimeGte)
  if (f.runtimeLte != null) p['with_runtime.lte'] = String(f.runtimeLte)
  if (f.language) p.with_original_language = f.language
  if (f.watchProviders.length > 0) {
    p.with_watch_providers = f.watchProviders.join('|')
    if (f.watchRegion) p.watch_region = f.watchRegion
  }
  if (f.status && f.kind === 'tv') p.with_status = f.status
  return p
}
