import { useMutation, useQuery } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Loader2 } from 'lucide-react'
import { qk } from '@/shared/api/query-keys'
import type {
  MetadataCandidate,
  MissingEpisodesReport,
  MissingSeasonsReport,
  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 { Card, CardContent, CardHeader, CardTitle } from '@/shared/ui/card'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/shared/ui/dialog'
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
import { toast } from '@/shared/ui/toast-store'
import { imageUrl } from '@/features/admin/images/api'
import { ImageGallery } from '@/features/admin/images/ImageGallery'
import {
  applyMetadata,
  clearMetadata,
  findMissingEpisodes,
  findMissingSeasons,
  getMetadataProviders,
  refreshEpisodesMetadata,
  renameShow,
  searchMetadata,
  setShowOriginalName,
  setShowPoster,
  updateMetadata,
} from './api'

export function ShowMetadataCard({
  show,
  onChanged,
}: Readonly<{ show: ShowDto; onChanged: () => void }>) {
  const { t } = useTranslation()
  const [galleryOpen, setGalleryOpen] = useState(false)
  const [provider, setProvider] = useState('')
  const [name, setName] = useState(show.name)
  const [originalName, setOriginalName] = useState(show.originalName ?? '')
  const [results, setResults] = useState<MetadataCandidate[]>([])
  const [searched, setSearched] = useState(false)
  const [description, setDescription] = useState(show.description ?? '')
  const [year, setYear] = useState(show.year != null ? String(show.year) : '')

  // После «Применить»/обновления с сервера показанные значения меняются — подхватываем их в поля формы
  // (иначе описание/год оставались бы пустыми, хотя в БД уже записаны). Реагируем только на смену
  // серверных значений, так что ручной ввод между сохранениями не затирается.
  useEffect(() => {
    setDescription(show.description ?? '')
    setYear(show.year != null ? String(show.year) : '')
  }, [show.description, show.year])

  const { data: providers } = useQuery({
    queryKey: qk.metadata.providers,
    queryFn: getMetadataProviders,
  })

  const onError = useApiError()
  const changed = () => onChanged()

  const setPoster = useMutation({
    mutationFn: (imageId: string) => setShowPoster(show.id, imageId),
    onSuccess: () => {
      toast.success(t('settings.saved'))
      changed()
    },
    onError,
  })

  const effectiveProvider = provider || providers?.[0] || ''
  // Метаданные ищем по оригинальному названию (иначе — по обычному).
  const searchTerm = originalName.trim() || name.trim()

  const search = useMutation({
    mutationFn: () => searchMetadata(effectiveProvider, searchTerm, show.kind),
    onSuccess: (data) => {
      setResults(data)
      setSearched(true)
    },
    onError,
  })
  const apply = useMutation({
    mutationFn: (externalId: string) => applyMetadata(show.id, effectiveProvider, externalId),
    onSuccess: () => {
      setResults([])
      setSearched(false)
      toast.success(t('admin.metadata.applied'))
      changed()
    },
    onError,
  })
  // Одна кнопка «Сохранить»: пишем только изменённое. Оригинальное название идёт своим эндпоинтом
  // (не трогает привязку к источнику), описание/год — ручной правкой метаданных.
  const save = useMutation({
    mutationFn: async () => {
      const yearNum = year.trim() ? Number(year) : null
      const infoChanged =
        (description.trim() || null) !== (show.description ?? null) ||
        yearNum !== (show.year ?? null)
      if (name.trim() && name.trim() !== show.name) await renameShow(show.id, name.trim())
      if (originalName.trim() !== (show.originalName ?? ''))
        await setShowOriginalName(show.id, originalName.trim() || null)
      if (infoChanged)
        await updateMetadata(show.id, { description: description.trim() || null, year: yearNum })
    },
    onSuccess: () => {
      toast.success(t('settings.saved'))
      changed()
    },
    onError,
  })
  const clear = useMutation({
    mutationFn: () => clearMetadata(show.id),
    onSuccess: () => {
      setDescription('')
      setYear('')
      changed()
    },
    onError,
  })
  const refreshEpisodes = useMutation({
    mutationFn: () => refreshEpisodesMetadata(show.id),
    onSuccess: (count) => {
      toast.success(t('admin.metadata.refreshedCount', { count }))
      onChanged()
    },
    onError,
  })
  const [missing, setMissing] = useState<MissingEpisodesReport | null>(null)
  const findMissing = useMutation({
    mutationFn: () => findMissingEpisodes(show.id),
    onSuccess: (report) => setMissing(report),
    onError,
  })
  const [missingSeasons, setMissingSeasons] = useState<MissingSeasonsReport | null>(null)
  const findSeasons = useMutation({
    mutationFn: () => findMissingSeasons(show.id),
    onSuccess: (report) => setMissingSeasons(report),
    onError,
  })

  const linked =
    !!show.metadataExternalId && !!show.metadataProvider && show.metadataProvider !== 'manual'

  return (
    <>
      <Card>
        <CardHeader>
          <CardTitle>{t('admin.metadata.title')}</CardTitle>
        </CardHeader>
        <CardContent className="flex flex-col gap-4 sm:flex-row">
          {/* Постер */}
          <div className="flex w-40 shrink-0 flex-col gap-2">
            <div className="flex aspect-[2/3] items-center justify-center overflow-hidden rounded-md border border-border bg-muted/30">
              {show.posterImageId ? (
                <img
                  src={imageUrl(show.posterImageId)}
                  alt=""
                  className="h-full w-full object-cover"
                />
              ) : (
                <span className="text-xs text-muted-foreground">
                  {t('admin.metadata.noPoster')}
                </span>
              )}
            </div>
            <Button size="sm" variant="outline" onClick={() => setGalleryOpen(true)}>
              {t('admin.metadata.pickPoster')}
            </Button>
            <ImageGallery
              open={galleryOpen}
              onOpenChange={setGalleryOpen}
              category="ShowPoster"
              onSelect={(img) => setPoster.mutate(img.id)}
            />
          </div>

          {/* Название + поиск + ручная правка */}
          <div className="flex min-w-0 flex-1 flex-col gap-4">
            <div className="flex flex-col gap-1.5">
              <Label>{t('admin.metadata.name')}</Label>
              <Input value={name} maxLength={256} onChange={(e) => setName(e.target.value)} />
            </div>

            <div className="flex flex-col gap-1.5">
              <Label>{t('admin.metadata.originalName')}</Label>
              <Input
                value={originalName}
                maxLength={256}
                placeholder={t('admin.metadata.originalNamePlaceholder')}
                onChange={(e) => {
                  setOriginalName(e.target.value)
                  setSearched(false)
                }}
              />
              <p className="text-xs text-muted-foreground">
                {t('admin.metadata.originalNameHint')}
              </p>
            </div>

            {providers && providers.length > 0 && (searched || results.length > 0) && (
              <div className="flex flex-col gap-2">
                {searched && results.length === 0 && (
                  <p className="text-xs text-muted-foreground">
                    {t('admin.metadata.nothingFound')}
                  </p>
                )}

                {results.length > 0 && (
                  <ul className="crt-panel max-h-72 divide-y divide-border overflow-y-auto rounded-md">
                    {results.map((r) => (
                      <li key={r.externalId} className="flex items-start gap-3 p-2">
                        {r.posterUrl ? (
                          <img
                            src={r.posterUrl}
                            alt=""
                            className="h-16 w-11 shrink-0 rounded object-cover"
                          />
                        ) : (
                          <div className="h-16 w-11 shrink-0 rounded bg-muted/40" />
                        )}
                        <div className="min-w-0 flex-1">
                          <div className="flex flex-wrap items-center gap-2 text-sm font-medium">
                            <span>
                              {r.title}
                              {r.year != null && (
                                <span className="text-muted-foreground"> ({r.year})</span>
                              )}
                            </span>
                            {/* В выдаче OMDb сериалы и полнометражки идут вперемешку — без метки
                              одноимённые фильм и сериал не различить. */}
                            {r.kind && (
                              <Badge variant="muted">{t(`admin.shows.kinds.${r.kind}`)}</Badge>
                            )}
                          </div>
                          {r.overview && (
                            <p className="line-clamp-2 text-xs text-muted-foreground">
                              {r.overview}
                            </p>
                          )}
                        </div>
                        <Button
                          size="sm"
                          variant="outline"
                          disabled={apply.isPending}
                          onClick={() => apply.mutate(r.externalId)}
                        >
                          {t('admin.metadata.apply')}
                        </Button>
                      </li>
                    ))}
                  </ul>
                )}
              </div>
            )}

            <div className="flex flex-col gap-2">
              <div className="flex items-end gap-2">
                <div className="flex flex-1 flex-col gap-1.5">
                  <Label>{t('admin.metadata.overview')}</Label>
                  <textarea
                    className="min-h-20 w-full rounded-sm border border-border bg-transparent px-3 py-2 text-sm"
                    value={description}
                    onChange={(e) => setDescription(e.target.value)}
                  />
                </div>
                <div className="flex w-24 flex-col gap-1.5">
                  <Label>{t('admin.metadata.year')}</Label>
                  <Input type="number" value={year} onChange={(e) => setYear(e.target.value)} />
                </div>
              </div>
              <div className="flex flex-wrap items-center gap-2">
                {providers && providers.length > 0 && (
                  <>
                    <Select value={effectiveProvider} onValueChange={setProvider}>
                      <SelectTrigger className="h-9 w-24">
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        {providers.map((p) => (
                          <SelectItem key={p} value={p}>
                            {p.toUpperCase()}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <Button
                      size="sm"
                      variant="outline"
                      disabled={search.isPending || !searchTerm}
                      onClick={() => search.mutate()}
                    >
                      {t('admin.metadata.searchBtn')}
                    </Button>
                  </>
                )}
                <Button
                  size="sm"
                  disabled={save.isPending || !name.trim()}
                  onClick={() => save.mutate()}
                >
                  {t('common.save')}
                </Button>
                {linked && (
                  <Button
                    size="sm"
                    variant="outline"
                    disabled={refreshEpisodes.isPending}
                    onClick={() => refreshEpisodes.mutate()}
                  >
                    {refreshEpisodes.isPending && <Loader2 className="h-4 w-4 animate-spin" />}
                    {refreshEpisodes.isPending
                      ? t('admin.metadata.refreshing')
                      : t('admin.metadata.refreshEpisodes')}
                  </Button>
                )}
                {linked && (
                  <Button
                    size="sm"
                    variant="outline"
                    disabled={findMissing.isPending}
                    onClick={() => findMissing.mutate()}
                  >
                    {findMissing.isPending && <Loader2 className="h-4 w-4 animate-spin" />}
                    {t('admin.metadata.findMissing')}
                  </Button>
                )}
                {linked && show.kind === 'Series' && (
                  <Button
                    size="sm"
                    variant="outline"
                    disabled={findSeasons.isPending}
                    onClick={() => findSeasons.mutate()}
                  >
                    {findSeasons.isPending && <Loader2 className="h-4 w-4 animate-spin" />}
                    {t('admin.metadata.findMissingSeasons')}
                  </Button>
                )}
                {(show.metadataProvider || show.posterImageId) && (
                  <Button
                    size="sm"
                    variant="ghost"
                    disabled={clear.isPending}
                    onClick={() => clear.mutate()}
                  >
                    {t('admin.metadata.clear')}
                  </Button>
                )}
                {show.metadataProvider && (
                  <span className="text-xs text-muted-foreground">
                    {t('admin.metadata.sourceLabel')}: {show.metadataProvider}
                  </span>
                )}
              </div>
            </div>
          </div>
        </CardContent>
      </Card>

      <Dialog open={missing != null} onOpenChange={(open) => !open && setMissing(null)}>
        <DialogContent className="max-w-lg">
          <DialogHeader>
            <DialogTitle>{t('admin.metadata.missingTitle')}</DialogTitle>
          </DialogHeader>
          {missing?.seasons.length === 0 && (
            <p className="text-sm text-muted-foreground">{t('admin.metadata.missingNoSeasons')}</p>
          )}
          {missing && missing.seasons.length > 0 && (
            <div className="flex max-h-[60vh] flex-col gap-3 overflow-y-auto">
              {missing.seasons.map((s) => (
                <div key={s.season} className="rounded-md border border-border p-3 text-sm">
                  <div className="flex flex-wrap items-center gap-2">
                    <span className="font-medium">
                      {t('admin.metadata.seasonN', { n: s.season })}
                    </span>
                    <span className="text-xs text-muted-foreground">
                      {t('admin.metadata.loadedOf', {
                        loaded: s.loaded,
                        total: s.expected ?? '?',
                      })}
                    </span>
                  </div>
                  <SeasonGapNote gap={s} />
                </div>
              ))}
            </div>
          )}
        </DialogContent>
      </Dialog>

      <Dialog
        open={missingSeasons != null}
        onOpenChange={(open) => !open && setMissingSeasons(null)}
      >
        <DialogContent className="max-w-lg">
          <DialogHeader>
            <DialogTitle>{t('admin.metadata.missingSeasonsTitle')}</DialogTitle>
          </DialogHeader>
          {missingSeasons && <MissingSeasonsBody report={missingSeasons} />}
        </DialogContent>
      </Dialog>
    </>
  )
}

/** Итог по сезонам: чего нет вовсе, что загружено сверх известного источнику. */
function MissingSeasonsBody({ report }: Readonly<{ report: MissingSeasonsReport }>) {
  const { t } = useTranslation()

  if (report.expected == null)
    return (
      <p className="text-sm text-amber-500">{t('admin.metadata.missingSeasonsUnknownTotal')}</p>
    )

  return (
    <div className="flex flex-col gap-2 text-sm">
      <p className="text-xs text-muted-foreground">
        {t('admin.metadata.loadedOf', { loaded: report.loaded.length, total: report.expected })}
      </p>
      {report.missing.length === 0 ? (
        <p className="text-emerald-500">{t('admin.metadata.missingSeasonsNone')}</p>
      ) : (
        <p className="text-muted-foreground">
          {t('admin.metadata.missingSeasonsList')}:{' '}
          <span className="text-foreground">{report.missing.join(', ')}</span>
        </p>
      )}
      {report.unknown.length > 0 && (
        <p className="text-xs text-amber-500">
          {t('admin.metadata.missingSeasonsUnknown', { list: report.unknown.join(', ') })}
        </p>
      )}
    </div>
  )
}

/** Итог по сезону: чего не хватает — или что полный состав сезона неизвестен. */
function SeasonGapNote({ gap }: Readonly<{ gap: MissingEpisodesReport['seasons'][number] }>) {
  const { t } = useTranslation()

  if (gap.expected == null)
    return <p className="mt-1 text-xs text-amber-500">{t('admin.metadata.missingUnknown')}</p>

  if (gap.missing.length === 0)
    return <p className="mt-1 text-xs text-emerald-500">{t('admin.metadata.missingNone')}</p>

  return (
    <p className="mt-1 text-muted-foreground">
      {t('admin.metadata.missingList')}:{' '}
      <span className="text-foreground">{gap.missing.join(', ')}</span>
    </p>
  )
}
