import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { Link } from '@tanstack/react-router'
import { Plus } from 'lucide-react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { qk } from '@/shared/api/query-keys'
import type { ShowKind, ShowSummaryDto } from '@/shared/api/types'
import { useApiError } from '@/shared/lib/use-api-error'
import { Badge } from '@/shared/ui/badge'
import { Button } from '@/shared/ui/button'
import { Input } from '@/shared/ui/input'
import { Pager } from '@/shared/ui/pager'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
import { sortRows, useTableSort } from '@/shared/lib/table-sort'
import { SortHeader } from '@/shared/ui/sortable'
import { listGenres } from '@/features/admin/genres/api'
import { deleteShow, listShows } from './api'
import { CreateShowDialog } from './CreateShowDialog'
import { DeleteShowDialog } from './DeleteShowDialog'
import { BulkTagBar } from './BulkTagBar'

const PAGE_SIZE = 20

export function ShowsPanel() {
  const { t } = useTranslation()
  const queryClient = useQueryClient()
  const [creating, setCreating] = useState(false)
  const [query, setQuery] = useState('')
  const [page, setPage] = useState(1)
  const { sort, toggle } = useTableSort('name', false)
  const sortColumn = (key: string) => {
    setPage(1)
    toggle(key)
  }

  // Фильтр по жанру — серверный: в списке видно только основной жанр, а отбирать нужно и по остальным.
  const [genreFilter, setGenreFilter] = useState('all')
  // Фильтр по типу — клиентский: тип уже приехал в списке, и гонять за ним запрос незачем.
  const [kindFilter, setKindFilter] = useState<'all' | ShowKind>('all')
  // Отмеченные для массовых действий. Живут отдельно от фильтров: сузил список — выбор не потерялся.
  const [selected, setSelected] = useState<string[]>([])
  const { data: genres } = useQuery({ queryKey: qk.genres.all, queryFn: listGenres })
  const { data, isLoading } = useQuery({
    queryKey: qk.shows.byGenre(genreFilter),
    queryFn: () => listShows(genreFilter === 'all' ? undefined : genreFilter),
  })

  // Список шоу обычно умещается в одну загрузку — фильтруем, сортируем и листаем на клиенте.
  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase()
    const all = data ?? []
    const matched = q
      ? all.filter(
          (s) =>
            s.name.toLowerCase().includes(q) || (s.originalName ?? '').toLowerCase().includes(q),
        )
      : all
    const byKind = kindFilter === 'all' ? matched : matched.filter((s) => s.kind === kindFilter)
    return sortRows(byKind, sort, {
      name: (s) => s.name.toLowerCase(),
      kind: (s) => s.kind,
      // Без рейтинга — в начало: пустое значение сортируется раньше любого кода.
      audience: (s) => s.audience ?? '',
      genre: (s) => (s.primaryGenre ?? '').toLowerCase(),
      seasons: (s) => s.seasonCount,
      episodes: (s) => s.episodeCount,
    })
  }, [data, query, kindFilter, sort])
  const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE))
  const pageItems = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE)
  const invalidate = () => queryClient.invalidateQueries({ queryKey: qk.shows.all })
  const onError = useApiError()

  // Отметки страницей и по одной. Вынесены из разметки: три вложенных стрелки прямо в onChange
  // читаются хуже, чем имя действия.
  const pageAllSelected =
    pageItems.length > 0 && pageItems.every((show) => selected.includes(show.id))

  const togglePage = (checked: boolean) =>
    setSelected((current) => {
      const ids = pageItems.map((show) => show.id)
      return checked
        ? [...new Set([...current, ...ids])]
        : current.filter((id) => !ids.includes(id))
    })

  const toggleShow = (showId: string) =>
    setSelected((current) =>
      current.includes(showId) ? current.filter((id) => id !== showId) : [...current, showId],
    )

  // Шоу к удалению: сначала спрашиваем про файлы, и только потом удаляем.
  const [toDelete, setToDelete] = useState<ShowSummaryDto | null>(null)
  const deleteMutation = useMutation({
    mutationFn: ({
      id,
      withMedia,
      withSchedule,
    }: {
      id: string
      withMedia: boolean
      withSchedule: boolean
    }) => deleteShow(id, withMedia, withSchedule),
    onSuccess: () => {
      setToDelete(null)
      invalidate()
    },
    onError,
  })

  return (
    <div className="flex flex-col gap-4">
      <div className="flex flex-wrap items-center justify-between gap-2">
        <h2 className="crt-glow text-xl font-semibold">{t('admin.shows.title')}</h2>
        <Button size="sm" onClick={() => setCreating(true)}>
          <Plus className="h-4 w-4" />
          {t('common.create')}
        </Button>
      </div>

      <div className="flex flex-wrap items-center gap-2">
        <Input
          className="max-w-xs"
          placeholder={t('common.search')}
          value={query}
          onChange={(e) => {
            setPage(1)
            setQuery(e.target.value)
          }}
        />
        <Select
          value={kindFilter}
          onValueChange={(v) => {
            setPage(1)
            setKindFilter(v as 'all' | ShowKind)
          }}
        >
          <SelectTrigger className="w-48">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="all">{t('admin.shows.allKinds')}</SelectItem>
            <SelectItem value="Series">{t('admin.shows.kinds.Series')}</SelectItem>
            <SelectItem value="Single">{t('admin.shows.kinds.Single')}</SelectItem>
          </SelectContent>
        </Select>
        <Select
          value={genreFilter}
          onValueChange={(v) => {
            setPage(1)
            setGenreFilter(v)
          }}
        >
          <SelectTrigger className="w-48">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="all">{t('admin.shows.allGenres')}</SelectItem>
            {(genres ?? []).map((genre) => (
              <SelectItem key={genre.id} value={genre.id}>
                {genre.name}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
      </div>

      {selected.length > 0 && <BulkTagBar showIds={selected} onDone={() => setSelected([])} />}

      <div className="crt-panel overflow-x-auto rounded-md">
        <table className="w-full text-sm">
          <thead className="border-b border-border text-left text-muted-foreground">
            <tr>
              <th className="w-8 px-4 py-2">
                <input
                  type="checkbox"
                  aria-label={t('admin.shows.bulk.selectPage')}
                  checked={pageAllSelected}
                  onChange={(e) => togglePage(e.target.checked)}
                />
              </th>
              <SortHeader
                label={t('admin.shows.name')}
                sortKey="name"
                sort={sort}
                onToggle={sortColumn}
              />
              <SortHeader
                label={t('admin.shows.kind')}
                sortKey="kind"
                sort={sort}
                onToggle={sortColumn}
              />
              <SortHeader
                label={t('admin.shows.audience')}
                sortKey="audience"
                sort={sort}
                onToggle={sortColumn}
              />
              <SortHeader
                label={t('admin.shows.genre')}
                sortKey="genre"
                sort={sort}
                onToggle={sortColumn}
              />
              <SortHeader
                label={t('admin.shows.seasons')}
                sortKey="seasons"
                sort={sort}
                onToggle={sortColumn}
              />
              <SortHeader
                label={t('admin.shows.episodes')}
                sortKey="episodes"
                sort={sort}
                onToggle={sortColumn}
              />
              <th className="px-4 py-2 font-medium">{t('common.actions')}</th>
            </tr>
          </thead>
          <tbody>
            {isLoading && (
              <tr>
                <td className="px-4 py-3 text-muted-foreground" colSpan={8}>
                  {t('common.loading')}
                </td>
              </tr>
            )}
            {pageItems.map((show) => (
              <tr key={show.id} className="border-b border-border last:border-0">
                <td className="px-4 py-2">
                  <input
                    type="checkbox"
                    aria-label={show.name}
                    checked={selected.includes(show.id)}
                    onChange={() => toggleShow(show.id)}
                  />
                </td>
                <td className="px-4 py-2">
                  <Link
                    to="/admin/shows/$showId"
                    params={{ showId: show.id }}
                    className="text-primary hover:underline"
                  >
                    {show.name}
                  </Link>
                </td>
                <td className="px-4 py-2">
                  <Badge variant="muted">{t(`admin.shows.kinds.${show.kind}`)}</Badge>
                </td>
                <td className="px-4 py-2">
                  {/* В таблице показываем сам код MPAA: он короткий и одинаков во всех локалях,
                      а расшифровка есть в селекте на карточке шоу. */}
                  {show.audience ? (
                    <Badge variant="muted">{show.audience}</Badge>
                  ) : (
                    <span className="text-muted-foreground">—</span>
                  )}
                </td>
                <td className="px-4 py-2 text-muted-foreground">
                  <GenreCell primary={show.primaryGenre} others={show.otherGenres} />
                </td>
                <td className="px-4 py-2 text-muted-foreground">{show.seasonCount}</td>
                <td className="px-4 py-2 text-muted-foreground">{show.episodeCount}</td>
                <td className="px-4 py-2">
                  <Button size="sm" variant="destructive" onClick={() => setToDelete(show)}>
                    {t('common.delete')}
                  </Button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <Pager page={page} totalPages={totalPages} onChange={setPage} />

      {creating && <CreateShowDialog onClose={() => setCreating(false)} onCreated={invalidate} />}

      {toDelete && (
        <DeleteShowDialog
          show={toDelete}
          pending={deleteMutation.isPending}
          onConfirm={(options) => deleteMutation.mutate({ id: toDelete.id, ...options })}
          onClose={() => setToDelete(null)}
        />
      )}
    </div>
  )
}

/** Основной жанр шоу; остальные — под кнопкой «…», всплывающим списком. */
function GenreCell({ primary, others }: Readonly<{ primary: string | null; others: string[] }>) {
  const { t } = useTranslation()
  const buttonRef = useRef<HTMLButtonElement>(null)
  // Всплывашку позиционируем fixed по кнопке: таблица лежит в контейнере с overflow-x-auto,
  // и absolute-список он обрезал бы по нижней границе. Прокрутка/ресайз сдвинули бы её мимо
  // кнопки — на них просто закрываем.
  const [at, setAt] = useState<{ left: number; top: number } | null>(null)

  useEffect(() => {
    if (at == null) return
    const close = () => setAt(null)
    window.addEventListener('scroll', close, true)
    window.addEventListener('resize', close)
    return () => {
      window.removeEventListener('scroll', close, true)
      window.removeEventListener('resize', close)
    }
  }, [at])

  if (!primary) return <span>—</span>
  if (others.length === 0) return <span>{primary}</span>

  const toggle = () => {
    if (at) {
      setAt(null)
      return
    }
    const rect = buttonRef.current?.getBoundingClientRect()
    if (rect) setAt({ left: rect.left, top: rect.bottom + 4 })
  }

  return (
    <span className="inline-flex items-center gap-1.5">
      {primary}
      <button
        ref={buttonRef}
        type="button"
        title={t('admin.shows.otherGenres')}
        aria-label={t('admin.shows.otherGenres')}
        aria-expanded={at != null}
        className="rounded-sm border border-border px-1 text-xs leading-tight hover:border-primary/60 hover:text-foreground"
        onClick={toggle}
        onBlur={() => setAt(null)}
        onKeyDown={(e) => e.key === 'Escape' && setAt(null)}
      >
        …
      </button>
      {at && (
        <div
          className="crt-popover fixed z-50 max-w-56 rounded-md px-2 py-1.5 text-xs"
          style={{ left: at.left, top: at.top }}
        >
          <p className="mb-1 text-[10px] uppercase tracking-wide text-muted-foreground">
            {t('admin.shows.otherGenres')}
          </p>
          <ul className="flex flex-col gap-0.5 text-foreground">
            {others.map((genre) => (
              <li key={genre}>{genre}</li>
            ))}
          </ul>
        </div>
      )}
    </span>
  )
}
