import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { Link } from '@tanstack/react-router'
import Hls from 'hls.js'
import { type ReactNode, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ChevronDown, ChevronLeft, RefreshCw } from 'lucide-react'
import { imageUrl } from '@/features/admin/images/api'
import { ImageGallery } from '@/features/admin/images/ImageGallery'
import { getAccessToken, HttpError } from '@/shared/api/client'
import type {
  AdInsertion,
  BlockMode,
  BumperFont,
  BumperSelection,
  BumperSettings,
  BumperTemplateDto,
  BumperTextKind,
  BumperTextVariantDto,
  BumperTrigger,
  ChannelShowDto,
  OverrideMode,
  ScheduleEntryDto,
} from '@/shared/api/types'
import { Badge } from '@/shared/ui/badge'
import { Button } from '@/shared/ui/button'
import { Card, CardContent, CardTitle } from '@/shared/ui/card'
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 { listMedia } from '@/features/admin/media/api'
import { listShows } from '@/features/admin/shows/api'
import {
  addBumperTemplate,
  addBumperVariant,
  addChannelAd,
  addChannelShow,
  bumperPreviewPlaylistUrl,
  clearBumperTemplateAudio,
  clearBumperTemplateBackground,
  createOverride,
  deleteOverride,
  getChannel,
  getSchedule,
  regenerateSchedule,
  removeBumperTemplate,
  removeBumperVariant,
  removeChannelAd,
  removeChannelShow,
  renderBumperPreview,
  setBumperTemplateBackground,
  updateBumperTemplate,
  updateBumperVariant,
  updateChannelSettings,
  updateChannelShow,
  uploadBumperTemplateAudio,
} from './api'

function formatTime(iso: string) {
  return new Date(iso).toLocaleString([], {
    day: '2-digit',
    month: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  })
}

export function ChannelDetail({ channelId }: { channelId: string }) {
  const { t } = useTranslation()
  const queryClient = useQueryClient()

  const { data: channel, isLoading } = useQuery({
    queryKey: ['admin', 'channels', channelId],
    queryFn: () => getChannel(channelId),
  })
  const { data: shows } = useQuery({ queryKey: ['admin', 'shows'], queryFn: listShows })
  const { data: ready } = useQuery({
    queryKey: ['admin', 'media', 'ready'],
    queryFn: () => listMedia({ page: 1, pageSize: 100, statuses: ['Ready'] }),
  })
  const { data: schedule } = useQuery({
    queryKey: ['admin', 'channels', channelId, 'schedule'],
    queryFn: () => getSchedule(channelId, new Date(), new Date(Date.now() + 12 * 3600_000)),
  })

  const invalidate = () => {
    void queryClient.invalidateQueries({ queryKey: ['admin', 'channels', channelId] })
  }
  const invalidateSchedule = () =>
    queryClient.invalidateQueries({ queryKey: ['admin', 'channels', channelId, 'schedule'] })
  const onError = (error: unknown) =>
    toast.error(error instanceof HttpError ? error.detail : t('common.error'))

  const regenerateMutation = useMutation({
    mutationFn: () => regenerateSchedule(channelId),
    onSuccess: () => {
      toast.success(t('admin.channels.regenerated'))
      void invalidateSchedule()
    },
    onError,
  })

  if (isLoading || !channel) return <p className="text-muted-foreground">{t('common.loading')}</p>

  const availableShows = shows?.filter((s) => !channel.shows.some((cs) => cs.showId === s.id)) ?? []

  return (
    <div className="flex flex-col gap-6">
      <div className="flex flex-wrap items-center justify-between gap-2">
        <div className="flex items-center gap-2">
          <Button asChild size="sm" variant="ghost">
            <Link to="/admin/channels">
              <ChevronLeft className="h-4 w-4" />
              {t('admin.channels.title')}
            </Link>
          </Button>
          <h2 className="crt-glow text-xl font-semibold">{channel.name}</h2>
          <Badge variant="muted">{channel.slug}</Badge>
        </div>
        <Button
          size="sm"
          variant="outline"
          disabled={regenerateMutation.isPending}
          onClick={() => regenerateMutation.mutate()}
        >
          <RefreshCw className="h-4 w-4" />
          {t('admin.channels.regenerate')}
        </Button>
      </div>

      <SettingsCard channel={channel} readyAssets={ready?.items ?? []} onSaved={invalidate} onError={onError} />

      <BumperCard channel={channel} onSaved={invalidate} onError={onError} />

      {/* Шоу канала */}
      <CollapsibleCard title={t('admin.channels.shows')} contentClassName="flex flex-col gap-3">
          <AddShowForm
            channelId={channelId}
            options={availableShows.map((s) => ({ id: s.id, name: s.name }))}
            onAdded={invalidate}
            onError={onError}
          />
          <table className="w-full text-sm">
            <thead className="border-b border-border text-left text-muted-foreground">
              <tr>
                <th className="py-2 font-medium">{t('admin.channels.show')}</th>
                <th className="py-2 font-medium">{t('admin.channels.weight')}</th>
                <th className="py-2 font-medium">{t('admin.channels.block')}</th>
                <th className="py-2 font-medium">{t('admin.channels.on')}</th>
                <th className="py-2 font-medium">{t('common.actions')}</th>
              </tr>
            </thead>
            <tbody>
              {channel.shows.map((row) => (
                <ChannelShowRow
                  key={row.id}
                  channelId={channelId}
                  row={row}
                  onChanged={invalidate}
                  onError={onError}
                />
              ))}
              {channel.shows.length === 0 && (
                <tr>
                  <td className="py-3 text-muted-foreground" colSpan={5}>
                    {t('admin.channels.noShows')}
                  </td>
                </tr>
              )}
            </tbody>
          </table>
      </CollapsibleCard>

      {/* Реклама */}
      <CollapsibleCard title={t('admin.channels.ads')} contentClassName="flex flex-col gap-3">
          <AddAdForm
            channelId={channelId}
            options={(ready?.items ?? [])
              .filter((a) => !channel.ads.some((ad) => ad.mediaAssetId === a.id))
              .map((a) => ({ id: a.id, name: a.originalFileName }))}
            onAdded={invalidate}
            onError={onError}
          />
          <ul className="flex flex-col divide-y divide-border">
            {channel.ads.map((ad) => (
              <li key={ad.id} className="flex items-center justify-between py-2 text-sm">
                <span>{ad.assetName ?? '—'}</span>
                <RemoveButton
                  onClick={() =>
                    removeChannelAd(channelId, ad.id).then(invalidate).catch(onError)
                  }
                />
              </li>
            ))}
            {channel.ads.length === 0 && (
              <li className="py-2 text-muted-foreground">{t('admin.channels.noAds')}</li>
            )}
          </ul>
      </CollapsibleCard>

      {/* Override'ы / марафоны */}
      <CollapsibleCard title={t('admin.channels.overrides')} contentClassName="flex flex-col gap-3">
          <OverrideForm
            channelId={channelId}
            options={channel.shows.map((cs) => ({ id: cs.showId, name: cs.showName }))}
            onCreated={invalidate}
            onError={onError}
          />
          <ul className="flex flex-col divide-y divide-border">
            {channel.overrides.map((o) => (
              <li key={o.id} className="flex items-center justify-between py-2 text-sm">
                <span>
                  <Badge variant="muted">{t(`admin.channels.modes.${o.mode}`)}</Badge>{' '}
                  {formatTime(o.startsAtUtc)} – {formatTime(o.endsAtUtc)} ·{' '}
                  {o.shows.map((s) => s.showName).join(', ')}
                </span>
                <RemoveButton
                  onClick={() => deleteOverride(channelId, o.id).then(invalidate).catch(onError)}
                />
              </li>
            ))}
            {channel.overrides.length === 0 && (
              <li className="py-2 text-muted-foreground">{t('admin.channels.noOverrides')}</li>
            )}
          </ul>
      </CollapsibleCard>

      {/* Предпросмотр расписания */}
      <CollapsibleCard title={t('admin.channels.schedule')}>
        <SchedulePreview entries={schedule ?? []} />
      </CollapsibleCard>
    </div>
  )
}

/** Карточка со сворачиваемым содержимым: клик по заголовку скрывает/раскрывает блок. */
function CollapsibleCard({
  title,
  defaultOpen = false,
  contentClassName,
  children,
}: {
  title: string
  defaultOpen?: boolean
  contentClassName?: string
  children: ReactNode
}) {
  const [open, setOpen] = useState(defaultOpen)
  return (
    <Card>
      <button
        type="button"
        onClick={() => setOpen((o) => !o)}
        aria-expanded={open}
        className="flex w-full items-center justify-between gap-2 p-6 text-left"
      >
        <CardTitle>{title}</CardTitle>
        <ChevronDown
          className={`h-5 w-5 shrink-0 text-muted-foreground transition-transform ${open ? 'rotate-180' : ''}`}
        />
      </button>
      {open && <CardContent className={contentClassName}>{children}</CardContent>}
    </Card>
  )
}

function SettingsCard({
  channel,
  readyAssets,
  onSaved,
  onError,
}: {
  channel: import('@/shared/api/types').ChannelDto
  readyAssets: { id: string; originalFileName: string }[]
  onSaved: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [name, setName] = useState(channel.name)
  const [isEnabled, setIsEnabled] = useState(channel.isEnabled)
  const [adInsertion, setAdInsertion] = useState<AdInsertion>(channel.adInsertion)
  const [adsPerBreak, setAdsPerBreak] = useState(channel.adsPerBreak)
  const [fillerAssetId, setFillerAssetId] = useState(channel.fillerAssetId ?? '')

  useEffect(() => {
    setName(channel.name)
    setIsEnabled(channel.isEnabled)
    setAdInsertion(channel.adInsertion)
    setAdsPerBreak(channel.adsPerBreak)
    setFillerAssetId(channel.fillerAssetId ?? '')
  }, [channel])

  const save = useMutation({
    mutationFn: () =>
      updateChannelSettings(channel.id, {
        name: name.trim(),
        isEnabled,
        adInsertion,
        adsPerBreak,
        // Заставки правятся в отдельной карточке — здесь передаём сохранённые значения без изменений.
        bumpersEnabled: channel.bumpersEnabled,
        bumper: channel.bumper,
        fillerAssetId: fillerAssetId || null,
      }),
    onSuccess: () => {
      toast.success(t('settings.saved'))
      onSaved()
    },
    onError,
  })

  return (
    <CollapsibleCard
      title={t('admin.channels.settings')}
      defaultOpen
      contentClassName="grid gap-4 sm:grid-cols-2"
    >
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.name')}</Label>
          <Input value={name} onChange={(e) => setName(e.target.value)} />
        </div>
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.adPolicy')}</Label>
          <Select value={adInsertion} onValueChange={(v) => setAdInsertion(v as AdInsertion)}>
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="BetweenBlocks">{t('admin.channels.betweenBlocks')}</SelectItem>
              <SelectItem value="BetweenEpisodes">{t('admin.channels.betweenEpisodes')}</SelectItem>
            </SelectContent>
          </Select>
        </div>
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.adsPerBreak')}</Label>
          <Input
            type="number"
            min={0}
            max={10}
            value={adsPerBreak}
            onChange={(e) => setAdsPerBreak(Number(e.target.value))}
          />
        </div>
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.filler')}</Label>
          <Select value={fillerAssetId || 'none'} onValueChange={(v) => setFillerAssetId(v === 'none' ? '' : v)}>
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="none">{t('admin.channels.noFiller')}</SelectItem>
              {readyAssets.map((a) => (
                <SelectItem key={a.id} value={a.id}>
                  {a.originalFileName}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
        <label className="flex items-center gap-2 text-sm">
          <input
            type="checkbox"
            checked={isEnabled}
            onChange={(e) => setIsEnabled(e.target.checked)}
          />
          {t('admin.channels.enabledLabel')}
        </label>
        <div className="flex items-end justify-end sm:col-span-2">
          <Button size="sm" disabled={save.isPending} onClick={() => save.mutate()}>
            {t('common.save')}
          </Button>
        </div>
    </CollapsibleCard>
  )
}

/** ffmpeg-цвет (0xRRGGBB / имя) → CSS для превью-плашки. */
function cssColor(value: string): string {
  const v = value.trim()
  if (v.startsWith('0x')) return `#${v.slice(2)}`
  return v
}

function BumperCard({
  channel,
  onSaved,
  onError,
}: {
  channel: import('@/shared/api/types').ChannelDto
  onSaved: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [bumpersEnabled, setBumpersEnabled] = useState(channel.bumpersEnabled)
  const [bumper, setBumper] = useState<BumperSettings>(channel.bumper)

  const setField = <K extends keyof BumperSettings>(key: K, value: BumperSettings[K]) =>
    setBumper((prev) => ({ ...prev, [key]: value }))

  useEffect(() => {
    setBumpersEnabled(channel.bumpersEnabled)
    setBumper(channel.bumper)
  }, [channel])

  // Общие настройки заставок сохраняются тем же эндпоинтом, что и настройки канала — остальные
  // поля берём из канала без изменений (они правятся в своей карточке).
  const save = useMutation({
    mutationFn: () =>
      updateChannelSettings(channel.id, {
        name: channel.name,
        isEnabled: channel.isEnabled,
        adInsertion: channel.adInsertion,
        adsPerBreak: channel.adsPerBreak,
        bumpersEnabled,
        bumper,
        fillerAssetId: channel.fillerAssetId,
      }),
    onSuccess: () => {
      toast.success(t('settings.saved'))
      onSaved()
    },
    onError,
  })

  const addTemplate = useMutation({
    mutationFn: () => addBumperTemplate(channel.id, ''),
    onSuccess: onSaved,
    onError,
  })

  const templates = [...channel.bumperTemplates].sort((a, b) => a.position - b.position)

  return (
    <CollapsibleCard title={t('admin.channels.bumpers')} contentClassName="flex flex-col gap-4">
      <label className="flex items-start gap-2 text-sm">
        <input
          type="checkbox"
          className="mt-1"
          checked={bumpersEnabled}
          onChange={(e) => setBumpersEnabled(e.target.checked)}
        />
        <span>
          {t('admin.channels.bumpersLabel')}
          <span className="block text-xs text-muted-foreground">
            {t('admin.channels.bumpersHint')}
          </span>
        </span>
      </label>

      {/* Общие настройки */}
      <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.bumperSelection')}</Label>
          <Select
            value={bumper.selection}
            onValueChange={(v) => setField('selection', v as BumperSelection)}
          >
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="Rotation">{t('admin.channels.bumperSelectionRotation')}</SelectItem>
              <SelectItem value="Random">{t('admin.channels.bumperSelectionRandom')}</SelectItem>
              <SelectItem value="AlwaysFirst">
                {t('admin.channels.bumperSelectionAlwaysFirst')}
              </SelectItem>
            </SelectContent>
          </Select>
        </div>
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.bumperFont')}</Label>
          <Select value={bumper.font} onValueChange={(v) => setField('font', v as BumperFont)}>
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="Sans">{t('admin.channels.bumperFontSans')}</SelectItem>
              <SelectItem value="Serif">{t('admin.channels.bumperFontSerif')}</SelectItem>
            </SelectContent>
          </Select>
        </div>
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.bumperMinInterval')}</Label>
          <Input
            type="number"
            min={0}
            max={1440}
            value={bumper.minIntervalMinutes}
            onChange={(e) => setField('minIntervalMinutes', Number(e.target.value))}
          />
        </div>
      </div>
      <div className="flex justify-end">
        <Button size="sm" disabled={save.isPending} onClick={() => save.mutate()}>
          {t('common.save')}
        </Button>
      </div>

      {/* Блоки заставок */}
      <div className="border-t border-border pt-4">
        <p className="text-sm font-medium">{t('admin.channels.bumperTemplates')}</p>
        <p className="text-xs text-muted-foreground">{t('admin.channels.bumperTemplatesHint')}</p>
      </div>
      <div className="flex flex-col gap-3">
        {templates.map((template) => (
          <BumperTemplateEditor
            key={template.id}
            channelId={channel.id}
            template={template}
            onChanged={onSaved}
            onError={onError}
          />
        ))}
      </div>
      <div className="flex justify-center">
        <Button
          size="sm"
          variant="outline"
          disabled={addTemplate.isPending}
          onClick={() => addTemplate.mutate()}
        >
          {t('admin.channels.bumperAddTemplate')}
        </Button>
      </div>
    </CollapsibleCard>
  )
}

function BumperTemplateEditor({
  channelId,
  template,
  onChanged,
  onError,
}: {
  channelId: string
  template: BumperTemplateDto
  onChanged: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [open, setOpen] = useState(false)
  const [name, setName] = useState(template.name)
  const [colors, setColors] = useState({
    backgroundColor: template.backgroundColor,
    backgroundColor2: template.backgroundColor2,
    accentColor: template.accentColor,
    textColor: template.textColor,
  })

  useEffect(() => {
    setName(template.name)
    setColors({
      backgroundColor: template.backgroundColor,
      backgroundColor2: template.backgroundColor2,
      accentColor: template.accentColor,
      textColor: template.textColor,
    })
  }, [template])

  const save = useMutation({
    mutationFn: () =>
      updateBumperTemplate(channelId, template.id, { name: name.trim(), ...colors }),
    onSuccess: () => {
      toast.success(t('settings.saved'))
      onChanged()
    },
    onError,
  })
  const remove = useMutation({
    mutationFn: () => removeBumperTemplate(channelId, template.id),
    onSuccess: onChanged,
    onError,
  })
  const addVariant = useMutation({
    mutationFn: () => addBumperVariant(channelId, template.id, ''),
    onSuccess: onChanged,
    onError,
  })

  const colorFields: { key: keyof typeof colors; label: string }[] = [
    { key: 'backgroundColor', label: t('admin.channels.bumperBg') },
    { key: 'backgroundColor2', label: t('admin.channels.bumperBg2') },
    { key: 'accentColor', label: t('admin.channels.bumperAccent') },
    { key: 'textColor', label: t('admin.channels.bumperText') },
  ]

  return (
    <div className="flex flex-col gap-3 rounded-md border border-border bg-muted/30 p-4">
      <div
        className="flex cursor-pointer items-center justify-between gap-2"
        onClick={() => setOpen((o) => !o)}
      >
        <div className="flex flex-wrap items-center gap-2">
          <ChevronDown
            className={`h-4 w-4 shrink-0 text-muted-foreground transition-transform ${open ? 'rotate-180' : ''}`}
          />
          <span className="text-sm font-medium">{template.name}</span>
          {template.isDefault && <Badge variant="muted">{t('admin.channels.bumperDefault')}</Badge>}
          <span className="text-xs text-muted-foreground">
            {template.hasAudio && template.audioDurationSeconds != null
              ? `≈${Math.round(template.audioDurationSeconds)} ${t('admin.channels.bumperSeconds')}`
              : t('admin.channels.bumperDefaultDuration')}
          </span>
        </div>
        {!template.isDefault && (
          <Button
            size="sm"
            variant="destructive"
            disabled={remove.isPending}
            onClick={(e) => {
              e.stopPropagation()
              remove.mutate()
            }}
          >
            {t('common.delete')}
          </Button>
        )}
      </div>

      {open && (
        <>
      <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.bumperTemplateName')}</Label>
          <Input value={name} maxLength={64} onChange={(e) => setName(e.target.value)} />
        </div>
        {colorFields.map(({ key, label }) => (
          <div key={key} className="flex flex-col gap-1.5">
            <Label>{label}</Label>
            <div className="flex items-center gap-2">
              <span
                className="h-8 w-8 shrink-0 rounded border border-border"
                style={{ backgroundColor: cssColor(colors[key]) }}
              />
              <Input
                value={colors[key]}
                onChange={(e) => setColors((c) => ({ ...c, [key]: e.target.value }))}
              />
            </div>
          </div>
        ))}
      </div>

      <div className="grid gap-3 border-t border-border pt-3 sm:grid-cols-2">
        <BumperFileUpload
          channelId={channelId}
          templateId={template.id}
          kind="audio"
          label={t('admin.channels.bumperAudio')}
          hint={t('admin.channels.bumperAudioHint')}
          has={template.hasAudio}
          accept="audio/*"
          upload={uploadBumperTemplateAudio}
          clear={clearBumperTemplateAudio}
          onSaved={onChanged}
          onError={onError}
        />
        <BumperBackgroundField
          channelId={channelId}
          templateId={template.id}
          backgroundImageId={template.backgroundImageId}
          onChanged={onChanged}
          onError={onError}
        />
      </div>

      {/* Подблоки (текст-варианты) */}
      <div className="flex flex-col gap-2 border-t border-border pt-3">
        <p className="text-sm font-medium">{t('admin.channels.bumperVariants')}</p>
        <p className="text-xs text-muted-foreground">{t('admin.channels.bumperVariantsHint')}</p>
        {[...template.variants]
          .sort((a, b) => a.position - b.position)
          .map((variant) => (
            <BumperVariantEditor
              key={variant.id}
              channelId={channelId}
              templateId={template.id}
              variant={variant}
              canRemove={template.variants.length > 1}
              onChanged={onChanged}
              onError={onError}
            />
          ))}
        <div>
          <Button
            size="sm"
            variant="outline"
            disabled={addVariant.isPending}
            onClick={() => addVariant.mutate()}
          >
            {t('admin.channels.bumperAddVariant')}
          </Button>
        </div>
      </div>

      <div className="flex flex-col gap-2 border-t border-border pt-3">
        <BumperPreviewPlayer channelId={channelId} templateId={template.id} onError={onError} />
      </div>

      <div className="flex justify-end">
        <Button size="sm" disabled={save.isPending} onClick={() => save.mutate()}>
          {t('common.save')}
        </Button>
      </div>
        </>
      )}
    </div>
  )
}

function BumperVariantEditor({
  channelId,
  templateId,
  variant,
  canRemove,
  onChanged,
  onError,
}: {
  channelId: string
  templateId: string
  variant: BumperTextVariantDto
  canRemove: boolean
  onChanged: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [form, setForm] = useState({
    name: variant.name,
    kind: variant.kind,
    nowLabel: variant.nowLabel,
    nextLabel: variant.nextLabel,
    line1: variant.line1,
    line2: variant.line2,
    trigger: variant.trigger,
  })

  useEffect(() => {
    setForm({
      name: variant.name,
      kind: variant.kind,
      nowLabel: variant.nowLabel,
      nextLabel: variant.nextLabel,
      line1: variant.line1,
      line2: variant.line2,
      trigger: variant.trigger,
    })
  }, [variant])

  const set = <K extends keyof typeof form>(key: K, value: (typeof form)[K]) =>
    setForm((f) => ({ ...f, [key]: value }))

  const save = useMutation({
    mutationFn: () =>
      updateBumperVariant(channelId, templateId, variant.id, { ...form, name: form.name.trim() }),
    onSuccess: () => {
      toast.success(t('settings.saved'))
      onChanged()
    },
    onError,
  })
  const remove = useMutation({
    mutationFn: () => removeBumperVariant(channelId, templateId, variant.id),
    onSuccess: onChanged,
    onError,
  })

  return (
    <div className="flex flex-col gap-2 rounded-md border border-border bg-background/40 p-3">
      <div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.bumperVariantName')}</Label>
          <Input value={form.name} maxLength={64} onChange={(e) => set('name', e.target.value)} />
        </div>
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.bumperTextKind')}</Label>
          <Select value={form.kind} onValueChange={(v) => set('kind', v as BumperTextKind)}>
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="NowNext">{t('admin.channels.bumperKindNowNext')}</SelectItem>
              <SelectItem value="Free">{t('admin.channels.bumperKindFree')}</SelectItem>
            </SelectContent>
          </Select>
        </div>
        <div className="flex flex-col gap-1.5">
          <Label>{t('admin.channels.bumperTrigger')}</Label>
          <Select value={form.trigger} onValueChange={(v) => set('trigger', v as BumperTrigger)}>
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="OnShowChange">
                {t('admin.channels.bumperTriggerOnShowChange')}
              </SelectItem>
              <SelectItem value="BetweenEpisodes">
                {t('admin.channels.bumperTriggerBetweenEpisodes')}
              </SelectItem>
              <SelectItem value="Both">{t('admin.channels.bumperTriggerBoth')}</SelectItem>
            </SelectContent>
          </Select>
        </div>
      </div>

      <div className="grid gap-2 sm:grid-cols-2">
        {form.kind === 'NowNext' ? (
          <>
            <div className="flex flex-col gap-1.5">
              <Label>{t('admin.channels.bumperNowLabel')}</Label>
              <Input
                value={form.nowLabel}
                maxLength={64}
                onChange={(e) => set('nowLabel', e.target.value)}
              />
            </div>
            <div className="flex flex-col gap-1.5">
              <Label>{t('admin.channels.bumperNextLabel')}</Label>
              <Input
                value={form.nextLabel}
                maxLength={64}
                onChange={(e) => set('nextLabel', e.target.value)}
              />
            </div>
          </>
        ) : (
          <>
            <div className="flex flex-col gap-1.5">
              <Label>{t('admin.channels.bumperLine1')}</Label>
              <Input
                value={form.line1}
                maxLength={120}
                onChange={(e) => set('line1', e.target.value)}
              />
            </div>
            <div className="flex flex-col gap-1.5">
              <Label>{t('admin.channels.bumperLine2')}</Label>
              <Input
                value={form.line2}
                maxLength={120}
                onChange={(e) => set('line2', e.target.value)}
              />
            </div>
          </>
        )}
      </div>

      <div className="flex items-center justify-end gap-2">
        {canRemove && (
          <Button
            size="sm"
            variant="ghost"
            disabled={remove.isPending}
            onClick={() => remove.mutate()}
          >
            {t('common.delete')}
          </Button>
        )}
        <Button
          size="sm"
          variant="outline"
          disabled={save.isPending || !form.name.trim()}
          onClick={() => save.mutate()}
        >
          {t('common.save')}
        </Button>
      </div>
    </div>
  )
}

function BumperPreviewPlayer({
  channelId,
  templateId,
  onError,
}: {
  channelId: string
  templateId: string
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const videoRef = useRef<HTMLVideoElement>(null)
  const [ready, setReady] = useState(false)
  const [bust, setBust] = useState(0)

  const render = useMutation({
    mutationFn: () => renderBumperPreview(channelId, templateId),
    onSuccess: () => {
      setBust(Date.now())
      setReady(true)
    },
    onError,
  })

  // Грузим отрендеренный превью-плейлист через hls.js, добавляя Bearer-токен (admin-роут под JWT).
  useEffect(() => {
    if (!ready) return
    const video = videoRef.current
    if (!video) return
    const src = `${bumperPreviewPlaylistUrl(channelId, templateId)}?t=${bust}`
    let hls: Hls | null = null
    if (Hls.isSupported()) {
      hls = new Hls({
        xhrSetup: (xhr) => {
          const token = getAccessToken()
          if (token) xhr.setRequestHeader('Authorization', `Bearer ${token}`)
        },
      })
      hls.loadSource(src)
      hls.attachMedia(video)
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = src
    }
    return () => {
      hls?.destroy()
    }
  }, [ready, bust, channelId, templateId])

  return (
    <>
      <div className="flex flex-wrap items-center gap-2">
        <Button
          size="sm"
          variant="outline"
          disabled={render.isPending}
          onClick={() => render.mutate()}
        >
          {render.isPending
            ? t('admin.channels.bumperPreviewRendering')
            : t('admin.channels.bumperPreview')}
        </Button>
        <span className="text-xs text-muted-foreground">
          {t('admin.channels.bumperPreviewHint')}
        </span>
      </div>
      {ready && (
        <video
          ref={videoRef}
          controls
          playsInline
          className="aspect-video w-full max-w-sm rounded-md border border-border bg-black"
        />
      )}
    </>
  )
}

function BumperBackgroundField({
  channelId,
  templateId,
  backgroundImageId,
  onChanged,
  onError,
}: {
  channelId: string
  templateId: string
  backgroundImageId: string | null
  onChanged: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [galleryOpen, setGalleryOpen] = useState(false)

  const setBg = useMutation({
    mutationFn: (imageId: string) => setBumperTemplateBackground(channelId, templateId, imageId),
    onSuccess: onChanged,
    onError,
  })
  const clearBg = useMutation({
    mutationFn: () => clearBumperTemplateBackground(channelId, templateId),
    onSuccess: onChanged,
    onError,
  })

  return (
    <div className="flex flex-col gap-1.5">
      <Label>
        {t('admin.channels.bumperBackground')}{' '}
        <span className={backgroundImageId ? 'text-emerald-500' : 'text-muted-foreground'}>
          {backgroundImageId
            ? `· ${t('admin.channels.bumperFileLoaded')}`
            : `· ${t('admin.channels.bumperFileDefault')}`}
        </span>
      </Label>
      <span className="text-xs text-muted-foreground">{t('admin.channels.bumperBackgroundHint')}</span>
      <div className="flex items-center gap-2">
        {backgroundImageId && (
          <img
            src={imageUrl(backgroundImageId)}
            alt=""
            className="h-10 w-16 shrink-0 rounded border border-border object-cover"
          />
        )}
        <Button size="sm" variant="outline" onClick={() => setGalleryOpen(true)}>
          {t('admin.channels.bumperBackgroundPick')}
        </Button>
        {backgroundImageId && (
          <Button size="sm" variant="ghost" disabled={clearBg.isPending} onClick={() => clearBg.mutate()}>
            {t('admin.channels.bumperReset')}
          </Button>
        )}
      </div>
      <ImageGallery
        open={galleryOpen}
        onOpenChange={setGalleryOpen}
        category="BumperBackground"
        onSelect={(img) => setBg.mutate(img.id)}
      />
    </div>
  )
}

function BumperFileUpload({
  channelId,
  templateId,
  kind,
  label,
  hint,
  has,
  accept,
  upload,
  clear,
  onSaved,
  onError,
}: {
  channelId: string
  templateId: string
  kind: string
  label: string
  hint: string
  has: boolean
  accept: string
  upload: (id: string, templateId: string, file: File) => Promise<void>
  clear: (id: string, templateId: string) => Promise<void>
  onSaved: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const inputId = `bumper-${kind}-${templateId}`

  const uploadMutation = useMutation({
    mutationFn: (file: File) => upload(channelId, templateId, file),
    onSuccess: onSaved,
    onError,
  })
  const clearMutation = useMutation({
    mutationFn: () => clear(channelId, templateId),
    onSuccess: onSaved,
    onError,
  })
  const busy = uploadMutation.isPending || clearMutation.isPending

  return (
    <div className="flex flex-col gap-1.5">
      <Label>
        {label}{' '}
        <span className={has ? 'text-emerald-500' : 'text-muted-foreground'}>
          {has ? `· ${t('admin.channels.bumperFileLoaded')}` : `· ${t('admin.channels.bumperFileDefault')}`}
        </span>
      </Label>
      <span className="text-xs text-muted-foreground">{hint}</span>
      <div className="flex items-center gap-2">
        <input
          id={inputId}
          type="file"
          accept={accept}
          className="hidden"
          onChange={(e) => {
            const file = e.target.files?.[0]
            if (file) uploadMutation.mutate(file)
            e.target.value = ''
          }}
        />
        <Button
          size="sm"
          variant="outline"
          disabled={busy}
          onClick={() => document.getElementById(inputId)?.click()}
        >
          {t('admin.channels.bumperUpload')}
        </Button>
        {has && (
          <Button size="sm" variant="ghost" disabled={busy} onClick={() => clearMutation.mutate()}>
            {t('admin.channels.bumperReset')}
          </Button>
        )}
      </div>
    </div>
  )
}

function AddShowForm({
  channelId,
  options,
  onAdded,
  onError,
}: {
  channelId: string
  options: { id: string; name: string }[]
  onAdded: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [showId, setShowId] = useState('')
  const [weight, setWeight] = useState(1)
  const [blockMode, setBlockMode] = useState<BlockMode>('Count')
  const [blockValue, setBlockValue] = useState(1)

  const add = useMutation({
    mutationFn: () => addChannelShow(channelId, { showId, weight, blockMode, blockValue }),
    onSuccess: () => {
      setShowId('')
      onAdded()
    },
    onError,
  })

  return (
    <div className="flex flex-wrap items-end gap-2">
      <Select value={showId} onValueChange={setShowId}>
        <SelectTrigger className="w-48">
          <SelectValue placeholder={t('admin.channels.pickShow')} />
        </SelectTrigger>
        <SelectContent>
          {options.map((o) => (
            <SelectItem key={o.id} value={o.id}>
              {o.name}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
      <NumberField label={t('admin.channels.weight')} value={weight} onChange={setWeight} min={1} />
      <Select value={blockMode} onValueChange={(v) => setBlockMode(v as BlockMode)}>
        <SelectTrigger className="w-36">
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="Count">{t('admin.channels.blockCount')}</SelectItem>
          <SelectItem value="Duration">{t('admin.channels.blockDuration')}</SelectItem>
        </SelectContent>
      </Select>
      <NumberField
        label={blockMode === 'Count' ? t('admin.channels.episodes') : t('admin.channels.minutes')}
        value={blockValue}
        onChange={setBlockValue}
        min={1}
      />
      <Button size="sm" disabled={!showId || add.isPending} onClick={() => add.mutate()}>
        {t('common.create')}
      </Button>
    </div>
  )
}

function ChannelShowRow({
  channelId,
  row,
  onChanged,
  onError,
}: {
  channelId: string
  row: ChannelShowDto
  onChanged: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [weight, setWeight] = useState(row.weight)
  const [blockMode, setBlockMode] = useState<BlockMode>(row.blockMode)
  const [blockValue, setBlockValue] = useState(row.blockValue)
  const [isEnabled, setIsEnabled] = useState(row.isEnabled)

  const save = useMutation({
    mutationFn: () =>
      updateChannelShow(channelId, row.id, { weight, blockMode, blockValue, isEnabled }),
    onSuccess: onChanged,
    onError,
  })

  return (
    <tr className="border-b border-border last:border-0">
      <td className="py-2">{row.showName}</td>
      <td className="py-2">
        <Input
          type="number"
          min={1}
          value={weight}
          onChange={(e) => setWeight(Number(e.target.value))}
          className="h-8 w-16"
        />
      </td>
      <td className="py-2">
        <div className="flex items-center gap-1">
          <Select value={blockMode} onValueChange={(v) => setBlockMode(v as BlockMode)}>
            <SelectTrigger className="h-8 w-36 whitespace-nowrap">
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="Count">{t('admin.channels.blockCount')}</SelectItem>
              <SelectItem value="Duration">{t('admin.channels.blockDuration')}</SelectItem>
            </SelectContent>
          </Select>
          <Input
            type="number"
            min={1}
            value={blockValue}
            onChange={(e) => setBlockValue(Number(e.target.value))}
            className="h-8 w-16"
          />
        </div>
      </td>
      <td className="py-2">
        <input
          type="checkbox"
          checked={isEnabled}
          onChange={(e) => setIsEnabled(e.target.checked)}
        />
      </td>
      <td className="py-2">
        <div className="flex gap-2">
          <Button size="sm" variant="outline" disabled={save.isPending} onClick={() => save.mutate()}>
            {t('common.save')}
          </Button>
          <RemoveButton
            onClick={() => removeChannelShow(channelId, row.id).then(onChanged).catch(onError)}
          />
        </div>
      </td>
    </tr>
  )
}

function AddAdForm({
  channelId,
  options,
  onAdded,
  onError,
}: {
  channelId: string
  options: { id: string; name: string }[]
  onAdded: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [assetId, setAssetId] = useState('')

  const add = useMutation({
    mutationFn: () => addChannelAd(channelId, assetId),
    onSuccess: () => {
      setAssetId('')
      onAdded()
    },
    onError,
  })

  return (
    <div className="flex flex-wrap items-end gap-2">
      <Select value={assetId} onValueChange={setAssetId}>
        <SelectTrigger className="max-w-md">
          <SelectValue placeholder={t('admin.channels.pickAd')} />
        </SelectTrigger>
        <SelectContent>
          {options.map((o) => (
            <SelectItem key={o.id} value={o.id}>
              {o.name}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
      <Button size="sm" disabled={!assetId || add.isPending} onClick={() => add.mutate()}>
        {t('common.create')}
      </Button>
    </div>
  )
}

function OverrideForm({
  channelId,
  options,
  onCreated,
  onError,
}: {
  channelId: string
  options: { id: string; name: string }[]
  onCreated: () => void
  onError: (e: unknown) => void
}) {
  const { t } = useTranslation()
  const [mode, setMode] = useState<OverrideMode>('Exclusive')
  const [showId, setShowId] = useState('')
  const [weight, setWeight] = useState(1)
  const [start, setStart] = useState('')
  const [end, setEnd] = useState('')

  const create = useMutation({
    mutationFn: () =>
      createOverride(channelId, {
        mode,
        startsAtUtc: new Date(start).toISOString(),
        endsAtUtc: new Date(end).toISOString(),
        shows: [{ showId, weight }],
      }),
    onSuccess: () => {
      setShowId('')
      setStart('')
      setEnd('')
      onCreated()
    },
    onError,
  })

  const valid = showId && start && end && new Date(end) > new Date(start)

  return (
    <div className="flex flex-wrap items-end gap-2">
      <Select value={mode} onValueChange={(v) => setMode(v as OverrideMode)}>
        <SelectTrigger className="w-36">
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="Exclusive">{t('admin.channels.modes.Exclusive')}</SelectItem>
          <SelectItem value="Boost">{t('admin.channels.modes.Boost')}</SelectItem>
        </SelectContent>
      </Select>
      <Select value={showId} onValueChange={setShowId}>
        <SelectTrigger className="w-44">
          <SelectValue placeholder={t('admin.channels.pickShow')} />
        </SelectTrigger>
        <SelectContent>
          {options.map((o) => (
            <SelectItem key={o.id} value={o.id}>
              {o.name}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
      {mode === 'Boost' && (
        <NumberField label={t('admin.channels.weight')} value={weight} onChange={setWeight} min={1} />
      )}
      <div className="flex flex-col gap-1.5">
        <Label>{t('admin.channels.from')}</Label>
        <Input type="datetime-local" value={start} onChange={(e) => setStart(e.target.value)} className="w-52" />
      </div>
      <div className="flex flex-col gap-1.5">
        <Label>{t('admin.channels.to')}</Label>
        <Input type="datetime-local" value={end} onChange={(e) => setEnd(e.target.value)} className="w-52" />
      </div>
      <Button size="sm" disabled={!valid || create.isPending} onClick={() => create.mutate()}>
        {t('common.create')}
      </Button>
    </div>
  )
}

function SchedulePreview({ entries }: { entries: ScheduleEntryDto[] }) {
  const { t } = useTranslation()
  if (entries.length === 0)
    return <p className="text-muted-foreground">{t('admin.channels.noSchedule')}</p>

  return (
    <ul className="flex flex-col divide-y divide-border text-sm">
      {entries.slice(0, 40).map((e) => (
        <li key={e.id} className="flex items-center gap-3 py-1.5">
          <span className="shrink-0 whitespace-nowrap tabular-nums text-muted-foreground">
            {formatTime(e.startsAtUtc)}
          </span>
          {e.kind === 'Ad' ? (
            <Badge variant="muted">{t('air.ad')}</Badge>
          ) : e.kind === 'Bumper' ? (
            <Badge variant="muted">{t('air.bumper')}</Badge>
          ) : (
            <span>
              {e.showName ?? '—'}
              {e.seasonEpisode ? (
                <span className="text-muted-foreground"> · {e.seasonEpisode}</span>
              ) : (
                e.episodeIndex != null && (
                  <span className="text-muted-foreground"> · {t('air.episode')} {e.episodeIndex + 1}</span>
                )
              )}
            </span>
          )}
        </li>
      ))}
    </ul>
  )
}

function NumberField({
  label,
  value,
  onChange,
  min,
}: {
  label: string
  value: number
  onChange: (v: number) => void
  min?: number
}) {
  return (
    <div className="flex flex-col gap-1.5">
      <Label>{label}</Label>
      <Input
        type="number"
        min={min}
        value={value}
        onChange={(e) => onChange(Number(e.target.value))}
        className="w-24"
      />
    </div>
  )
}

function RemoveButton({ onClick }: { onClick: () => void }) {
  const { t } = useTranslation()
  return (
    <Button size="sm" variant="destructive" onClick={onClick}>
      {t('common.delete')}
    </Button>
  )
}
