import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { Link } from '@tanstack/react-router'
import { ChevronLeft, Plus, Send } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { listAllMedia } from '@/features/admin/media/api'
import { HttpError } from '@/shared/api/client'
import type { GridLayerDto, SlotDto } from '@/shared/api/types'
import { Badge } from '@/shared/ui/badge'
import { Button } from '@/shared/ui/button'
import { Card, CardContent } from '@/shared/ui/card'
import { Input } from '@/shared/ui/input'
import { cn } from '@/shared/lib/cn'
import { toast } from '@/shared/ui/toast-store'
import {
  applyChannelTemplate,
  copyTemplateTo,
  createLayer,
  createSlot,
  deleteLayer,
  getChannel,
  getChannelTemplate,
  getSchedule,
  listChannels,
  toSlotBody,
  updateLayer,
  updateSlot,
} from './api'
import { ApplyDialog } from './components/ApplyDialog'
import { BumperCard } from './components/BumperCard'
import { EntryTraceDialog } from './components/EntryTraceDialog'
import { JunctionsCard } from './components/JunctionsCard'
import { LayerApplicabilityDialog } from './components/LayerApplicabilityDialog'
import { LayerList, ScheduleGrid } from './components/ScheduleGrid'
import { SchedulePreview } from './components/SchedulePreview'
import { RulesCard } from './components/RulesCard'
import { SettingsCard } from './components/SettingsCard'
import { TemplateIssues } from './components/TemplateIssues'
import { TemplatePreview } from './components/TemplatePreview'
import { ViewerCard } from './components/ViewerCard'
import { SlotInspector, type SlotDraft } from './components/SlotInspector'
import { toTime } from './lib/format'

/** Вкладки экрана канала — в порядке частоты правки. */
const TABS = ['grid', 'rules', 'junctions', 'bumpers', 'viewer', 'settings', 'air'] as const
type ChannelTab = (typeof TABS)[number]

export function ChannelDetail({ channelId }: { channelId: string }) {
  const { t } = useTranslation()
  const queryClient = useQueryClient()
  const [draft, setDraft] = useState<SlotDraft | null>(null)
  const [activeLayerId, setActiveLayerId] = useState<string | null>(null)
  const [viewDate, setViewDate] = useState<string>('')
  const [applicabilityLayer, setApplicabilityLayer] = useState<GridLayerDto | null>(null)
  // День, который копируем, и отмеченные дни-приёмники.
  const [copySource, setCopySource] = useState<number | null>(null)
  const [copyTargets, setCopyTargets] = useState<number[]>([])
  const [applyOpen, setApplyOpen] = useState(false)
  const [traceEntryId, setTraceEntryId] = useState<string | null>(null)
  const [copyToChannel, setCopyToChannel] = useState('')
  const [tab, setTab] = useState<ChannelTab>('grid')

  const { data: channel, isLoading } = useQuery({
    queryKey: ['admin', 'channels', channelId],
    queryFn: () => getChannel(channelId),
  })
  const { data: template, error: templateError } = useQuery({
    queryKey: ['admin', 'channels', channelId, 'template'],
    queryFn: () => getChannelTemplate(channelId),
  })
  const { data: ready } = useQuery({
    queryKey: ['admin', 'media', 'ready', 'all'],
    queryFn: () => listAllMedia({ 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 onError = (error: unknown) =>
    toast.error(error instanceof HttpError ? error.detail : t('common.error'))

  const { data: channels } = useQuery({ queryKey: ['admin', 'channels'], queryFn: listChannels })

  const applyMutation = useMutation({
    mutationFn: () => applyChannelTemplate(channelId),
    onSuccess: (result) => {
      setApplyOpen(false)
      toast.success(t('admin.channels.applied', { count: result.added }))
      // Предупреждения показываем по одному: каждое указывает на конкретный слот.
      for (const warning of result.warnings)
        toast.error(`${t(`admin.channels.warnings.${warning.kind}`)}: ${warning.details}`)
      invalidate()
    },
    onError,
  })

  const addLayerMutation = useMutation({
    mutationFn: () => {
      const nextPriority = Math.max(0, ...(template?.layers.map((l) => l.priority) ?? [0])) + 10
      return createLayer(template!.id, {
        name: t('admin.channels.newLayerName'),
        priority: nextPriority,
      })
    },
    onSuccess: invalidate,
    onError,
  })

  const deleteLayerMutation = useMutation({
    mutationFn: (layer: GridLayerDto) => deleteLayer(layer.id),
    onSuccess: invalidate,
    onError,
  })

  const toggleLayerMutation = useMutation({
    mutationFn: (layer: GridLayerDto) =>
      updateLayer(layer.id, {
        name: layer.name,
        priority: layer.priority,
        applicability: layer.applicability,
        isEnabled: !layer.isEnabled,
      }),
    onSuccess: invalidate,
    onError,
  })

  /**
   * Порядок слоёв задаётся перетаскиванием, а хранится приоритетом. Раздаём приоритеты с шагом 10
   * снизу вверх: шаг оставляет место, чтобы следующая вставка не переписывала весь список.
   */
  const reorderLayersMutation = useMutation({
    mutationFn: async (layerIdsTopFirst: string[]) => {
      const byId = new Map(template!.layers.map((l) => [l.id, l]))
      const total = layerIdsTopFirst.length
      await Promise.all(
        layerIdsTopFirst.map((id, index) => {
          const layer = byId.get(id)
          const priority = (total - index) * 10
          if (!layer || layer.priority === priority) return Promise.resolve()
          return updateLayer(id, {
            name: layer.name,
            priority,
            applicability: layer.applicability,
            isEnabled: layer.isEnabled,
          })
        }),
      )
    },
    onSuccess: invalidate,
    onError,
  })

  const copyTemplateMutation = useMutation({
    mutationFn: (targetChannelId: string) => copyTemplateTo(channelId, targetChannelId),
    onSuccess: (result) => {
      setCopyToChannel('')
      toast.success(
        t('admin.channels.templateCopied', { layers: result.layers, slots: result.slots }),
      )
      if (result.droppedBumperRefs > 0)
        toast.error(
          t('admin.channels.copyDroppedBumpers', { count: result.droppedBumperRefs }),
        )
    },
    onError,
  })

  const moveSlotMutation = useMutation({
    mutationFn: ({
      slot,
      weekday,
      startMinutes,
    }: {
      slot: SlotDto
      weekday: number
      startMinutes: number
    }) => updateSlot(slot.id, { ...toSlotBody(slot), weekday, targetStart: toTime(startMinutes) }),
    onSuccess: invalidate,
    onError,
  })

  const resizeSlotMutation = useMutation({
    mutationFn: ({ slot, minutes }: { slot: SlotDto; minutes: number }) =>
      updateSlot(slot.id, { ...toSlotBody(slot), targetDurationMinutes: minutes }),
    onSuccess: invalidate,
    onError,
  })

  /** Копирование дня: слоты «каждый день» не копируются — они и так есть во всех колонках. */
  const copyDayMutation = useMutation({
    mutationFn: async ({ from, to }: { from: number; to: number[] }) => {
      const sources = (template?.layers ?? []).flatMap((layer) =>
        layer.slots.filter((slot) => slot.weekday === from).map((slot) => ({ layer, slot })),
      )
      for (const weekday of to)
        for (const { layer, slot } of sources)
          await createSlot(layer.id, { ...toSlotBody(slot), weekday })
    },
    onSuccess: () => {
      setCopySource(null)
      invalidate()
    },
    onError,
  })

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

  const layerForNewSlot =
    activeLayerId ?? template?.layers.find((l) => !l.isBackground)?.id ?? template?.layers[0]?.id

  const openNewSlot = (weekday: number, startMinutes: number) => {
    if (!layerForNewSlot) return
    const hh = Math.floor(startMinutes / 60)
      .toString()
      .padStart(2, '0')
    const mm = (startMinutes % 60).toString().padStart(2, '0')
    setDraft({
      layerId: layerForNewSlot,
      slot: null,
      defaults: { weekday, targetStart: `${hh}:${mm}:00`, title: t('admin.channels.newSlot') },
    })
  }

  const openSlot = (slot: SlotDto) => setDraft({ layerId: slot.layerId, slot })

  return (
    <div className="flex flex-col gap-6">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <Button asChild size="sm" variant="ghost">
          <Link to="/admin/channels">
            <ChevronLeft className="h-4 w-4" />
            {t('admin.channels.title')}
          </Link>
        </Button>

        <div className="flex flex-wrap items-center gap-2">
          <h2 className="crt-glow text-xl font-semibold">{channel.name}</h2>
          {channel.number !== null && <Badge variant="muted">№ {channel.number}</Badge>}
          {!channel.isEnabled && <Badge variant="muted">{t('admin.channels.disabled')}</Badge>}
        </div>
      </div>

      {/* Правка правил эфира не двигает — применение отдельной кнопкой. */}
      {template?.hasPendingChanges && (
        <div className="flex flex-wrap items-center justify-between gap-3 rounded-md border border-amber-500/50 bg-amber-500/10 px-4 py-2 text-sm">
          <span>{t('admin.channels.pendingChanges')}</span>
          <Button size="sm" disabled={applyMutation.isPending} onClick={() => setApplyOpen(true)}>
            <Send className="h-4 w-4" /> {t('admin.channels.apply')}
          </Button>
        </div>
      )}

      {/* Вкладки вместо колонки карточек: экран канала перестал помещаться в один свиток. */}
      <nav className="flex flex-wrap gap-4 border-b border-border text-xs uppercase tracking-wide">
        {TABS.map((value) => (
          <button
            key={value}
            type="button"
            onClick={() => setTab(value)}
            className={cn(
              'pb-2 text-muted-foreground hover:text-foreground',
              tab === value && 'border-b-2 border-primary text-primary',
            )}
          >
            {t(`admin.channels.tabs.${value}`)}
          </button>
        ))}
      </nav>

      {tab === 'settings' && (
        <SettingsCard
          channel={channel}
          readyAssets={ready?.items ?? []}
          bare
          onSaved={invalidate}
          onError={onError}
        />
      )}

      {/* Шаблон мог не загрузиться — раньше вкладка сетки просто оказывалась пустой. */}
      {tab === 'grid' && !template && (
        <p className="text-sm text-muted-foreground">
          {templateError instanceof HttpError
            ? templateError.detail
            : t('admin.channels.noTemplate')}
        </p>
      )}

      {tab === 'grid' && template && (
        <Card>
          <CardContent>
            <div className="grid gap-4 lg:grid-cols-[220px_1fr]">
            <div className="flex flex-col gap-2">
              <div className="flex items-center justify-between gap-2">
                <h3 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                  {t('admin.channels.layers')}
                </h3>
                <Button
                  size="sm"
                  variant="ghost"
                  onClick={() => addLayerMutation.mutate()}
                  disabled={addLayerMutation.isPending}
                >
                  <Plus className="h-4 w-4" />
                </Button>
              </div>
              <LayerList
                template={template}
                activeLayerId={layerForNewSlot ?? null}
                viewDate={viewDate || null}
                onSelect={(layer) => setActiveLayerId(layer.id)}
                onDelete={(layer) => deleteLayerMutation.mutate(layer)}
                onToggle={(layer) => toggleLayerMutation.mutate(layer)}
                onReorder={(order) => reorderLayersMutation.mutate(order)}
                onEditApplicability={setApplicabilityLayer}
              />
              <p className="text-xs text-muted-foreground">{t('admin.channels.layersHint')}</p>

              {/* Копия сетки на другой канал: группы общие, поэтому переносятся только правила. */}
              <div className="flex flex-col gap-1.5 border-t border-border pt-2">
                <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                  {t('admin.channels.copyTemplate')}
                </span>
                <select
                  className="h-8 rounded-md border border-border bg-transparent px-2 text-sm"
                  value={copyToChannel}
                  onChange={(e) => setCopyToChannel(e.target.value)}
                >
                  <option value="">{t('admin.channels.pickTargetChannel')}</option>
                  {(channels ?? [])
                    .filter((c) => c.id !== channelId)
                    .map((c) => (
                      <option key={c.id} value={c.id}>
                        {c.name}
                      </option>
                    ))}
                </select>
                <Button
                  size="sm"
                  variant="outline"
                  disabled={!copyToChannel || copyTemplateMutation.isPending}
                  onClick={() => copyTemplateMutation.mutate(copyToChannel)}
                >
                  {t('admin.channels.copy')}
                </Button>
                <p className="text-xs text-muted-foreground">
                  {t('admin.channels.copyTemplateHint')}
                </p>
              </div>
            </div>

            <div className="flex flex-col gap-3">
              <TemplateIssues
                channelId={channelId}
                slotsById={
                  new Map(template.layers.flatMap((l) => l.slots).map((slot) => [slot.id, slot]))
                }
                onGoToSlot={openSlot}
              />
              <TemplatePreview channelId={channelId} />

              {/* Сетка на конкретную дату: видно, какие слои в этот день действительно действуют. */}
              <div className="flex flex-wrap items-center gap-2 text-sm">
                <span className="text-muted-foreground">{t('admin.channels.showForDate')}</span>
                <Input
                  type="date"
                  className="h-8 w-40"
                  value={viewDate}
                  onChange={(e) => setViewDate(e.target.value)}
                />
                {viewDate && (
                  <Button size="sm" variant="ghost" onClick={() => setViewDate('')}>
                    {t('admin.channels.allDates')}
                  </Button>
                )}
              </div>

              {/* Копирование дня: сначала выбирается источник, потом дни-приёмники. */}
              {copySource !== null && (
                <div className="flex flex-wrap items-center gap-2 rounded-md border border-border px-3 py-2 text-sm">
                  <span>
                    {t('admin.channels.copyDayFrom', {
                      day: t(`admin.channels.weekdays.${copySource}`),
                    })}
                  </span>
                  {[1, 2, 3, 4, 5, 6, 0]
                    .filter((day) => day !== copySource)
                    .map((day) => (
                      <label key={day} className="flex items-center gap-1">
                        <input
                          type="checkbox"
                          checked={copyTargets.includes(day)}
                          onChange={(e) =>
                            setCopyTargets((current) =>
                              e.target.checked
                                ? [...current, day]
                                : current.filter((d) => d !== day),
                            )
                          }
                        />
                        {t(`admin.channels.weekdays.${day}`)}
                      </label>
                    ))}
                  <Button
                    size="sm"
                    disabled={copyTargets.length === 0 || copyDayMutation.isPending}
                    onClick={() =>
                      copyDayMutation.mutate({ from: copySource, to: copyTargets })
                    }
                  >
                    {t('admin.channels.copy')}
                  </Button>
                  <Button size="sm" variant="ghost" onClick={() => setCopySource(null)}>
                    {t('common.cancel')}
                  </Button>
                </div>
              )}

              <ScheduleGrid
                template={template}
                selectedSlotId={draft?.slot?.id ?? null}
                viewDate={viewDate || null}
                onSelectSlot={openSlot}
                onAddSlot={openNewSlot}
                onMoveSlot={(slot, weekday, startMinutes) =>
                  moveSlotMutation.mutate({ slot, weekday, startMinutes })
                }
                onResizeSlot={(slot, minutes) => resizeSlotMutation.mutate({ slot, minutes })}
                onCopyDay={(weekday) => {
                  setCopySource(weekday)
                  setCopyTargets([])
                }}
              />
              {draft && (
                <SlotInspector
                  channelId={channelId}
                  draft={draft}
                  onClose={() => setDraft(null)}
                  onChanged={invalidate}
                />
              )}
            </div>
            </div>
          </CardContent>
        </Card>
      )}

      {tab === 'rules' &&
        (template ? (
          <RulesCard template={template} bare onChanged={invalidate} onError={onError} />
        ) : (
          <p className="text-sm text-muted-foreground">{t('admin.channels.noTemplate')}</p>
        ))}

      {tab === 'junctions' && (
        <JunctionsCard
          channel={channel}
          template={template}
          bare
          onChanged={invalidate}
          onError={onError}
        />
      )}

      {tab === 'bumpers' && (
        <BumperCard channel={channel} bare onSaved={invalidate} onError={onError} />
      )}

      {tab === 'viewer' && (
        <ViewerCard channel={channel} bare onSaved={invalidate} onError={onError} />
      )}

      {tab === 'air' && (
        <Card>
          <CardContent>
            <SchedulePreview entries={schedule ?? []} onShowTrace={setTraceEntryId} />
          </CardContent>
        </Card>
      )}

      {applicabilityLayer && (
        <LayerApplicabilityDialog
          layer={applicabilityLayer}
          onClose={() => setApplicabilityLayer(null)}
          onChanged={invalidate}
          onError={onError}
        />
      )}

      {applyOpen && (
        <ApplyDialog
          channelId={channelId}
          utcOffsetMinutes={channel.utcOffsetMinutes}
          pending={applyMutation.isPending}
          onApply={() => applyMutation.mutate()}
          onClose={() => setApplyOpen(false)}
        />
      )}

      {traceEntryId && (
        <EntryTraceDialog
          entryId={traceEntryId}
          utcOffsetMinutes={channel.utcOffsetMinutes}
          onClose={() => setTraceEntryId(null)}
        />
      )}
    </div>
  )
}
