import { GripVertical, Plus, Trash2 } from 'lucide-react'
import { useRef } from 'react'
import { useTranslation } from 'react-i18next'
import type { BumperLineColor, BumperLineDto, BumperLineStyle } from '@/shared/api/types'
import { Button } from '@/shared/ui/button'
import { Input } from '@/shared/ui/input'
import { cn } from '@/shared/lib/cn'
import { useKeyedList } from '@/shared/lib/keyed-list'
import { hasVolatileToken, PLACEHOLDERS, resolveSample, unknownTokens } from '../placeholders'

const STYLES: BumperLineStyle[] = ['Label', 'Title', 'Caption']
const COLORS: BumperLineColor[] = ['Accent', 'Text']
const MAX_LINES = 6

/** Готовые наборы строк. Данные редактора, а не сущность: пресет просто заполняет список. */
function presets(t: (key: string) => string): { key: string; lines: BumperLineDto[] }[] {
  const label = (text: string): BumperLineDto => ({
    style: 'Label',
    color: 'Accent',
    text,
  })
  const title = (text: string): BumperLineDto => ({ style: 'Title', color: 'Text', text })
  return [
    {
      key: 'nowNext',
      lines: [
        label(t('admin.bumpers.presetNow')),
        title('{now.title}'),
        label(t('admin.bumpers.presetNext')),
        title('{next.title}'),
      ],
    },
    {
      key: 'nextAt',
      lines: [label(`${t('admin.bumpers.presetNextAt')} {next.time}`), title('{next.title}')],
    },
    {
      key: 'channel',
      lines: [title('{channel}'), { style: 'Caption', color: 'Accent', text: '{weekday}, {time}' }],
    },
  ]
}

/**
 * Строки заставки: порядок перетаскиванием, палитра плейсхолдеров под фокусированным полем,
 * пресеты кнопкой. Ошибка ввода (незнакомый плейсхолдер) видна сразу — сервер её всё равно
 * отвергнет, но узнавать об этом при сохранении неудобно.
 *
 * Список свой, со стабильными ключами строк (см. useKeyedList), а наружу уезжает только значение:
 * при индексных ключах удаление строки из середины уводило бы фокус в соседнюю. Пересев на другой
 * подблок, редактор пересоздаётся по `key` — поэтому props читаются только на первом рендере.
 */
export function BumperLinesEditor({
  lines: initial,
  onChange,
}: Readonly<{
  lines: BumperLineDto[]
  onChange: (lines: BumperLineDto[]) => void
}>) {
  const { t } = useTranslation()
  const { rows, reset, add, remove, patch } = useKeyedList(initial)
  const focused = useRef<string | null>(null)
  const inputs = useRef(new Map<string, HTMLInputElement | null>())
  const dragged = useRef<string | null>(null)

  const setLine = (key: string, part: Partial<BumperLineDto>) => {
    patch(key, (line) => ({ ...line, ...part }))
    // Правка уезжает наверх сразу: сохраняет подблок родитель, у него же лежит остальная форма.
    onChange(rows.map((row) => (row.key === key ? { ...row.value, ...part } : row.value)))
  }

  const addLine = () => {
    if (rows.length >= MAX_LINES) return
    const line: BumperLineDto = { style: 'Title', color: 'Text', text: '' }
    add(line)
    onChange([...rows.map((row) => row.value), line])
  }

  const removeLine = (key: string) => {
    remove(key)
    onChange(rows.filter((row) => row.key !== key).map((row) => row.value))
  }

  const applyPreset = (preset: BumperLineDto[]) => {
    reset(preset)
    onChange(preset)
  }

  const move = (fromKey: string, toKey: string) => {
    if (fromKey === toKey) return
    const next = [...rows]
    const from = next.findIndex((row) => row.key === fromKey)
    const to = next.findIndex((row) => row.key === toKey)
    if (from < 0 || to < 0) return

    const [row] = next.splice(from, 1)
    next.splice(to, 0, row)
    const values = next.map((r) => r.value)
    reset(values)
    onChange(values)
  }

  /** Вставка плейсхолдера в позицию курсора — иначе его пришлось бы допечатывать руками. */
  const insert = (token: string) => {
    const key = focused.current ?? rows.at(-1)?.key
    const row = rows.find((r) => r.key === key)
    if (!key || !row) return

    const input = inputs.current.get(key)
    const at = input?.selectionStart ?? row.value.text.length
    const text = row.value.text
    setLine(key, { text: `${text.slice(0, at)}{${token}}${text.slice(at)}` })
    requestAnimationFrame(() => {
      input?.focus()
      const caret = at + token.length + 2
      input?.setSelectionRange(caret, caret)
    })
  }

  return (
    <div className="flex flex-col gap-2">
      <div className="flex flex-wrap items-center gap-2">
        <span className="text-xs uppercase tracking-wide text-muted-foreground">
          {t('admin.bumpers.presets')}
        </span>
        {presets(t).map((preset) => (
          <Button
            key={preset.key}
            size="sm"
            variant="outline"
            onClick={() => applyPreset(preset.lines)}
          >
            {t(`admin.bumpers.preset_${preset.key}`)}
          </Button>
        ))}
      </div>

      {rows.map(({ key, value: line }) => {
        const unknown = unknownTokens(line.text)
        return (
          <div
            key={key}
            draggable
            onDragStart={() => {
              dragged.current = key
            }}
            onDragOver={(e) => e.preventDefault()}
            onDrop={() => {
              if (dragged.current !== null) move(dragged.current, key)
              dragged.current = null
            }}
            className="flex flex-wrap items-center gap-2 rounded-md border border-border bg-background/40 p-2"
          >
            <GripVertical className="h-4 w-4 shrink-0 cursor-grab text-muted-foreground" />
            <select
              className="h-8 rounded-md border border-border bg-transparent px-2 text-xs"
              value={line.style}
              onChange={(e) => setLine(key, { style: e.target.value as BumperLineStyle })}
            >
              {STYLES.map((style) => (
                <option key={style} value={style}>
                  {t(`admin.bumpers.lineStyles.${style}`)}
                </option>
              ))}
            </select>
            <select
              className="h-8 rounded-md border border-border bg-transparent px-2 text-xs"
              value={line.color}
              onChange={(e) => setLine(key, { color: e.target.value as BumperLineColor })}
            >
              {COLORS.map((color) => (
                <option key={color} value={color}>
                  {t(`admin.bumpers.lineColors.${color}`)}
                </option>
              ))}
            </select>
            <Input
              ref={(el) => {
                inputs.current.set(key, el)
              }}
              className={cn('h-8 min-w-40 flex-1', unknown.length > 0 && 'border-destructive')}
              value={line.text}
              maxLength={120}
              onFocus={() => {
                focused.current = key
              }}
              onChange={(e) => setLine(key, { text: e.target.value })}
            />
            <Button size="sm" variant="ghost" onClick={() => removeLine(key)}>
              <Trash2 className="h-4 w-4" />
            </Button>
            <div className="w-full pl-6 text-xs">
              {unknown.length > 0 ? (
                <span className="text-destructive">
                  {t('admin.bumpers.unknownPlaceholder', { tokens: unknown.join(', ') })}
                </span>
              ) : (
                <span className="text-muted-foreground">
                  → {resolveSample(line.text) || '—'}
                  {hasVolatileToken(line.text) && (
                    <span className="ml-2 text-amber-500">{t('admin.bumpers.volatileHint')}</span>
                  )}
                </span>
              )}
            </div>
          </div>
        )
      })}

      <div className="flex flex-wrap items-center gap-2">
        <Button size="sm" variant="outline" disabled={rows.length >= MAX_LINES} onClick={addLine}>
          <Plus className="h-4 w-4" /> {t('admin.bumpers.addLine')}
        </Button>
      </div>

      {/* Палитра: клик вставляет плейсхолдер в фокусированное поле, подсказка показывает образец. */}
      <div className="flex flex-wrap gap-1">
        {PLACEHOLDERS.map((placeholder) => {
          const description = t(`admin.bumpers.tokens.${placeholder.token}`)
          return (
            <button
              key={placeholder.token}
              type="button"
              title={`${description} → ${placeholder.sample}`}
              onClick={() => insert(placeholder.token)}
              className="rounded border border-border px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground hover:border-primary hover:text-foreground"
            >
              {`{${placeholder.token}}`}
            </button>
          )
        })}
      </div>
    </div>
  )
}
