import { useQuery } from '@tanstack/react-query'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Play, X } from 'lucide-react'
import { qk } from '@/shared/api/query-keys'
import type { MediaAssetDto, ShowDto } 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 {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/shared/ui/dialog'
import { Input } from '@/shared/ui/input'
import { HlsVideo } from '@/shared/ui/hls-video'
import { Pager } from '@/shared/ui/pager'
import { toast } from '@/shared/ui/toast-store'
import { listAllMedia, mediaPreviewUrl } from '@/features/admin/media/api'
import {
  type ParsedEpisode,
  compareParsed,
  formatSeasonEpisode,
  parseEpisodeName,
} from '@/features/admin/media/episode-parse'
import { addEpisode } from './api'

type Candidate = { asset: MediaAssetDto; parsed: ParsedEpisode }

const PAGE_SIZE = 20

/** Пока идёт пакетное добавление — прогресс вместо подписи; серии добавляются по одной. */
function addButtonLabel(
  progress: { current: number; total: number } | null,
  count: number,
  t: ReturnType<typeof useTranslation>['t'],
) {
  if (progress) return `${progress.current}/${progress.total}`
  return `${t('admin.shows.addSelected')} (${count})`
}

/**
 * Выбор готовых медиа для добавления в шоу. Живёт отдельным окном: список кандидатов — это вся
 * необработанная библиотека, и на странице шоу он заслонял собой и метаданные, и сами серии.
 * Заодно тяжёлый запрос за готовыми ассетами уходит только при открытии окна.
 */
export function AddEpisodesDialog({
  show,
  onClose,
  onAdded,
}: Readonly<{ show: ShowDto; onClose: () => void; onAdded: () => void }>) {
  const { t } = useTranslation()
  const onError = useApiError()
  const [filter, setFilter] = useState('')
  const [deselected, setDeselected] = useState<Set<string>>(new Set())
  const [adding, setAdding] = useState<{ current: number; total: number } | null>(null)
  const [page, setPage] = useState(1)
  // Предпросмотр встроен в окно, а не открывается поверх: выбирают по именам файлов, и смотреть
  // приходится подряд несколько — со второй модалкой это были бы два клика на каждый.
  const [preview, setPreview] = useState<MediaAssetDto | null>(null)

  const { data: ready, isLoading } = useQuery({
    queryKey: qk.media.ready,
    queryFn: () => listAllMedia({ statuses: ['Ready'] }),
  })

  // Кандидаты: готовые ассеты, ещё не добавленные в шоу, отфильтрованные по строке и упорядоченные
  // по распознанному номеру сезона/серии (нераспознанные — в конец по имени).
  const candidates = useMemo<Candidate[]>(() => {
    const existing = new Set(show.episodes.map((e) => e.mediaAssetId))
    const term = filter.trim().toLowerCase()
    return (ready?.items ?? [])
      .filter((a) => !existing.has(a.id))
      .filter((a) => !term || a.originalFileName.toLowerCase().includes(term))
      .map((asset) => ({ asset, parsed: parseEpisodeName(asset.originalFileName) }))
      .sort((a, b) =>
        compareParsed(
          { name: a.asset.originalFileName, parsed: a.parsed },
          { name: b.asset.originalFileName, parsed: b.parsed },
        ),
      )
  }, [show, ready, filter])

  const isSingle = show.kind === 'Single'
  const selected = candidates.filter((c) => !deselected.has(c.asset.id))

  // Страницу зажимаем в допустимый диапазон — чтобы после фильтрации не застрять на пустой.
  const totalPages = Math.max(1, Math.ceil(candidates.length / PAGE_SIZE))
  const pageSafe = Math.min(page, totalPages)
  const items = candidates.slice((pageSafe - 1) * PAGE_SIZE, pageSafe * PAGE_SIZE)

  const toggle = (id: string) =>
    setDeselected((prev) => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })

  const bulkAdd = async () => {
    // Полнометражке — максимум одна серия.
    const chosen = isSingle ? selected.slice(0, 1) : selected
    let added = 0
    for (let i = 0; i < chosen.length; i++) {
      setAdding({ current: i + 1, total: chosen.length })
      try {
        await addEpisode(show.id, chosen[i].asset.id)
        added++
      } catch (error) {
        onError(error)
      }
    }
    setAdding(null)
    setDeselected(new Set())
    onAdded()
    if (added > 0) {
      toast.success(t('admin.shows.addedCount', { count: added }))
      onClose()
    }
  }

  return (
    <Dialog open onOpenChange={(open) => !open && onClose()}>
      <DialogContent className="max-w-3xl">
        <DialogHeader>
          <DialogTitle>{t('admin.shows.addMedia')}</DialogTitle>
          <DialogDescription>{t('admin.shows.addMediaHint')}</DialogDescription>
        </DialogHeader>

        <div className="flex flex-col gap-2">
          <div className="flex flex-wrap items-center gap-2">
            <Input
              className="max-w-md"
              placeholder={t('admin.shows.filterAssets')}
              value={filter}
              onChange={(e) => {
                setPage(1)
                setFilter(e.target.value)
              }}
            />
            <Button size="sm" variant="outline" onClick={() => setDeselected(new Set())}>
              {t('admin.shows.selectAll')}
            </Button>
            <Button
              size="sm"
              variant="outline"
              onClick={() => setDeselected(new Set(candidates.map((c) => c.asset.id)))}
            >
              {t('admin.shows.deselectAll')}
            </Button>
          </div>

          <div className="crt-panel max-h-96 overflow-y-auto rounded-md">
            {isLoading && (
              <p className="px-4 py-3 text-sm text-muted-foreground">{t('common.loading')}</p>
            )}
            {!isLoading && candidates.length === 0 && (
              <p className="px-4 py-3 text-sm text-muted-foreground">
                {t('admin.shows.noMatches')}
              </p>
            )}
            {candidates.length > 0 && (
              <ul className="divide-y divide-border text-sm">
                {items.map(({ asset, parsed }) => {
                  const label = formatSeasonEpisode(parsed)
                  return (
                    <li key={asset.id} className="flex items-center gap-2 pr-2 hover:bg-muted">
                      {/* Кнопка вне label: внутри неё клик по «плею» заодно снимал бы галочку. */}
                      <label className="flex min-w-0 flex-1 cursor-pointer items-center gap-3 px-4 py-2">
                        <input
                          type="checkbox"
                          checked={!deselected.has(asset.id)}
                          onChange={() => toggle(asset.id)}
                        />
                        {label ? <Badge>{label}</Badge> : <Badge variant="muted">—</Badge>}
                        <span className="truncate">{asset.originalFileName}</span>
                      </label>
                      <Button
                        size="sm"
                        variant="ghost"
                        title={t('admin.media.preview')}
                        onClick={() => setPreview(asset)}
                      >
                        <Play className="h-4 w-4" />
                      </Button>
                    </li>
                  )
                })}
              </ul>
            )}
          </div>
          {preview && (
            <div className="crt-panel flex flex-col gap-2 rounded-md p-3">
              <div className="flex items-center gap-2">
                <span className="min-w-0 flex-1 truncate text-sm">{preview.originalFileName}</span>
                <Button size="sm" variant="ghost" onClick={() => setPreview(null)}>
                  <X className="h-4 w-4" />
                </Button>
              </div>
              <HlsVideo src={mediaPreviewUrl(preview.id)} autoPlay />
            </div>
          )}
          {ready?.truncated && (
            <p className="text-xs text-amber-500">{t('admin.shows.candidatesTruncated')}</p>
          )}
          <Pager page={pageSafe} totalPages={totalPages} onChange={setPage} />
        </div>

        <DialogFooter>
          <Button size="sm" variant="outline" onClick={onClose}>
            {t('common.cancel')}
          </Button>
          <Button
            size="sm"
            disabled={selected.length === 0 || adding != null}
            onClick={() => void bulkAdd()}
          >
            {addButtonLabel(adding, isSingle ? Math.min(1, selected.length) : selected.length, t)}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
