import { useQuery } from '@tanstack/react-query'
import { Eye } from 'lucide-react'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { qk } from '@/shared/api/query-keys'
import type { PlannedItemKind, PreviewItemDto, SchedulePreviewDto } from '@/shared/api/types'
import { Badge } from '@/shared/ui/badge'
import { Button } from '@/shared/ui/button'
import { cn } from '@/shared/lib/cn'
import { previewTemplate } from '../api'
import { channelTime, formatChannelTime } from '../lib/format'
import { toIsoDate } from '../lib/applicability'
import { PlanningWarnings } from './PlanningWarnings'

const KIND_COLORS: Record<PlannedItemKind, string> = {
  Program: 'bg-primary/70',
  Fallback: 'bg-muted-foreground/40',
  SignOff: 'bg-slate-500/60',
  Ad: 'bg-amber-500/70',
  Promo: 'bg-sky-500/70',
  Bumper: 'bg-violet-500/70',
}

/** Что видит зритель как программу — врезки в программу передач не попадают. */
const PROGRAMME_KINDS = new Set<PlannedItemKind>(['Program', 'Fallback', 'SignOff'])

/**
 * Предпросмотр (см. 6.4): прогон генератора по текущим правилам без записи и без продвижения
 * курсоров. Заставки приходят резервом известной длины — реальный рендер только при применении.
 */
export function TemplatePreview({ channelId }: Readonly<{ channelId: string }>) {
  const { t } = useTranslation()
  const [open, setOpen] = useState(false)
  const [days, setDays] = useState(1)
  const [tab, setTab] = useState<'programme' | 'tape' | 'problems'>('programme')

  const { data, isFetching } = useQuery({
    queryKey: qk.channels.preview(channelId, days),
    queryFn: () => previewTemplate(channelId, days),
    enabled: open,
    // Черновик правил может меняться между открытиями — кэшировать прогон смысла нет.
    staleTime: 0,
    gcTime: 0,
  })

  return (
    <div className="flex flex-col gap-3">
      <div className="flex flex-wrap items-center gap-2">
        <Button size="sm" variant="outline" onClick={() => setOpen((v) => !v)}>
          <Eye className="h-4 w-4" />
          {open ? t('admin.channels.previewHide') : t('admin.channels.preview')}
        </Button>
        {open && (
          <>
            <select
              className="h-8 rounded-md border border-border bg-transparent px-2 text-sm"
              value={days}
              onChange={(e) => setDays(Number(e.target.value))}
            >
              {[1, 3, 7].map((value) => (
                <option key={value} value={value}>
                  {t('admin.channels.previewDays', { count: value })}
                </option>
              ))}
            </select>
            <span className="text-xs text-muted-foreground">
              {isFetching ? t('common.loading') : t('admin.channels.previewHint')}
            </span>
          </>
        )}
      </div>

      {open && data && (
        <div className="flex flex-col gap-3">
          <div className="flex gap-2 border-b border-border text-xs uppercase tracking-wide">
            {(['programme', 'tape', 'problems'] as const).map((value) => (
              <button
                key={value}
                type="button"
                onClick={() => setTab(value)}
                className={cn(
                  'pb-1 text-muted-foreground hover:text-foreground',
                  tab === value && 'border-b-2 border-primary text-primary',
                )}
              >
                {t(`admin.channels.previewTabs.${value}`)}
                {value === 'problems' && data.warnings.length > 0 && ` · ${data.warnings.length}`}
              </button>
            ))}
          </div>

          {tab === 'programme' && <Programme preview={data} />}
          {tab === 'tape' && <Tape preview={data} />}
          {tab === 'problems' && <Problems preview={data} />}
        </div>
      )}
    </div>
  )
}

function Programme({ preview }: Readonly<{ preview: SchedulePreviewDto }>) {
  const { t } = useTranslation()
  const items = preview.items.filter((i) => PROGRAMME_KINDS.has(i.kind))

  if (items.length === 0)
    return <p className="text-sm text-muted-foreground">{t('admin.channels.noSchedule')}</p>

  return (
    <ul className="flex flex-col divide-y divide-border text-sm">
      {items.map((item, index) => (
        <li key={`${item.startsAtUtc}-${index}`} className="flex items-center gap-3 py-1.5">
          <span className="shrink-0 tabular-nums text-muted-foreground">
            {formatChannelTime(item.startsAtUtc, preview.utcOffsetMinutes)}
          </span>
          <span className="min-w-0 flex-1 truncate">
            {item.title ?? t(`admin.channels.previewKinds.${item.kind}`)}
          </span>
          {item.slotTitle && (
            <span className="shrink-0 text-xs text-muted-foreground">{item.slotTitle}</span>
          )}
        </li>
      ))}
    </ul>
  )
}

/** Час вещания → сколько в нём минут врезок. По ним же строится гистограмма нагрузки. */
function loadByHour(preview: SchedulePreviewDto): { hour: Date; minutes: number }[] {
  const buckets = new Map<number, number>()
  for (const item of preview.items) {
    if (item.kind !== 'Ad' && item.kind !== 'Promo' && item.kind !== 'Bumper') continue
    const start = channelTime(item.startsAtUtc, preview.utcOffsetMinutes)
    const hour = Date.UTC(
      start.getUTCFullYear(),
      start.getUTCMonth(),
      start.getUTCDate(),
      start.getUTCHours(),
    )
    const minutes =
      (new Date(item.endsAtUtc).getTime() - new Date(item.startsAtUtc).getTime()) / 60_000
    buckets.set(hour, (buckets.get(hour) ?? 0) + minutes)
  }
  return [...buckets.entries()]
    .sort((a, b) => a[0] - b[0])
    .map(([hour, minutes]) => ({ hour: new Date(hour), minutes }))
}

function Tape({ preview }: Readonly<{ preview: SchedulePreviewDto }>) {
  const { t } = useTranslation()
  const load = useMemo(() => loadByHour(preview), [preview])
  const peak = Math.max(1, ...load.map((l) => l.minutes))

  if (preview.items.length === 0)
    return <p className="text-sm text-muted-foreground">{t('admin.channels.noSchedule')}</p>

  return (
    <div className="flex flex-col gap-4">
      {load.length > 0 && (
        <div className="flex flex-col gap-1">
          <span className="text-xs text-muted-foreground">
            {t('admin.channels.previewLoad', { peak: Math.round(peak) })}
          </span>
          <div className="flex h-16 items-end gap-px">
            {load.map((bucket) => (
              <div
                key={bucket.hour.toISOString()}
                className="flex-1 bg-amber-500/70"
                style={{ height: `${(bucket.minutes / peak) * 100}%` }}
                title={`${String(bucket.hour.getUTCHours()).padStart(2, '0')}:00 · ${Math.round(bucket.minutes)} ${t('admin.groups.minutesShort')}`}
              />
            ))}
          </div>
        </div>
      )}

      <ul className="flex flex-col gap-0.5 text-xs">
        {preview.items.map((item, index) => (
          <TapeRow key={`${item.startsAtUtc}-${index}`} item={item} preview={preview} />
        ))}
      </ul>
    </div>
  )
}

function TapeRow({
  item,
  preview,
}: Readonly<{ item: PreviewItemDto; preview: SchedulePreviewDto }>) {
  const { t } = useTranslation()
  const minutes =
    (new Date(item.endsAtUtc).getTime() - new Date(item.startsAtUtc).getTime()) / 60_000

  return (
    <li className="flex items-center gap-2">
      <span className="w-10 shrink-0 tabular-nums text-muted-foreground">
        {formatChannelTime(item.startsAtUtc, preview.utcOffsetMinutes)}
      </span>
      <span
        className={cn('h-2 shrink-0 rounded-sm', KIND_COLORS[item.kind])}
        style={{ width: `${Math.max(4, minutes * 2)}px` }}
      />
      <Badge variant="muted" className="shrink-0">
        {t(`admin.channels.previewKinds.${item.kind}`)}
      </Badge>
      <span className="min-w-0 flex-1 truncate text-muted-foreground">{item.title ?? ''}</span>
    </li>
  )
}

/** Проблемы прогона: те же предупреждения, что и в отчёте о применении, плюс карта повторов. */
function Problems({ preview }: Readonly<{ preview: SchedulePreviewDto }>) {
  return (
    <div className="flex flex-col gap-4">
      <PlanningWarnings warnings={preview.warnings} />
      <RepeatHeatmap preview={preview} />
    </div>
  )
}

/**
 * Тепловая карта повторов: матрица «шоу × вещательные сутки», яркость — число показов. Сразу видно,
 * что один фильм крутится четыре раза за неделю.
 */
function RepeatHeatmap({ preview }: Readonly<{ preview: SchedulePreviewDto }>) {
  const { t } = useTranslation()

  const { days, rows } = useMemo(() => {
    const counts = new Map<string, Map<string, number>>()
    const dayKeys = new Set<string>()

    for (const item of preview.items) {
      if (item.kind !== 'Program' || !item.title) continue
      const day = toIsoDate(channelTime(item.startsAtUtc, preview.utcOffsetMinutes))
      dayKeys.add(day)
      const row = counts.get(item.title) ?? new Map<string, number>()
      row.set(day, (row.get(day) ?? 0) + 1)
      counts.set(item.title, row)
    }

    const sortedDays = [...dayKeys].sort((a, b) => a.localeCompare(b))
    const sortedRows = [...counts.entries()]
      .map(([title, byDay]) => ({
        title,
        byDay,
        total: [...byDay.values()].reduce((sum, n) => sum + n, 0),
      }))
      .sort((a, b) => b.total - a.total)
      .slice(0, 25)

    return { days: sortedDays, rows: sortedRows }
  }, [preview])

  if (rows.length === 0) return null

  const peak = Math.max(1, ...rows.flatMap((row) => [...row.byDay.values()]))

  return (
    <div className="flex flex-col gap-1">
      <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
        {t('admin.channels.heatmap')}
      </span>
      <div className="overflow-x-auto">
        <table className="text-[11px]">
          <thead className="text-muted-foreground">
            <tr>
              <th className="px-1 text-left font-medium" />
              {days.map((day) => (
                <th key={day} className="px-1 font-medium">
                  {day.slice(8)}.{day.slice(5, 7)}
                </th>
              ))}
              <th className="px-1 font-medium">{t('admin.channels.heatmapTotal')}</th>
            </tr>
          </thead>
          <tbody>
            {rows.map((row) => (
              <tr key={row.title}>
                <td className="max-w-56 truncate px-1" title={row.title}>
                  {row.title}
                </td>
                {days.map((day) => {
                  const count = row.byDay.get(day) ?? 0
                  return (
                    <td key={day} className="px-0.5 py-0.5">
                      <span
                        className="block h-4 w-6 rounded-sm bg-primary text-center text-[10px] leading-4"
                        style={{ opacity: count === 0 ? 0.06 : 0.25 + (count / peak) * 0.75 }}
                      >
                        {count > 0 ? count : ''}
                      </span>
                    </td>
                  )
                })}
                <td className="px-1 tabular-nums text-muted-foreground">{row.total}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  )
}
