import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ClockCounterClockwiseIcon, CaretLeftIcon, CaretRightIcon, XIcon } from '@phosphor-icons/react'
import { Badge, Dialog, DialogContent, DialogTitle, DialogDescription } from '@/ui'
import type { ChangelogEntry, DictionaryDetail, FlattenedRecord } from '@/api/client'
import { useChangelog, useChangelogDiff, useRecords } from '@/api/queries'
import { kindBadgeVariant, kindIcon, kindLabel } from '@/components/changelog/kind-meta'
import { ChangelogDiffModal } from '@/components/changelog/ChangelogDiffModal'
import { UserCell } from '@/lib/useUserDisplay'
import { cn } from '@/lib/utils'

/**
 * TimeTravelModal — fullscreen-y modal per redesign prototype.
 *
 * <p>Layout:
 * <ul>
 *   <li><b>Header</b>: clock icon + TIME-TRAVEL · {DICT} cap + h1 title + ×</li>
 *   <li><b>Compare panels</b>: СЕЙЧАС vs ТОЧКА ВО ВРЕМЕНИ side-by-side.
 *       Right panel highlighted (`tint`) когда target != current.</li>
 *   <li><b>Quick presets</b>: сейчас / неделя / месяц / год / при создании</li>
 *   <li><b>Step toggle</b>: версия | день + ‹ › nav arrows</li>
 *   <li><b>Slider</b>: HTML range + overlay version markers (top) + date
 *       markers (bottom). Cursor — dark circle.</li>
 *   <li><b>Tabs</b>: Что изменилось / Записи на момент / Структура и поля.
 *       «Что изменилось» — wired через {@code useChangelog} + filter
 *       events с {@code publishedAt >= picked}. Diff button открывает
 *       {@link ChangelogDiffModal} с full before/after JSON.</li>
 *   <li><b>Footer</b>: disclaimer + Закрыть / "Открыть v{X} как readonly" CTA</li>
 * </ul>
 *
 * <p><b>Backend coverage:</b> findActiveAt(at) уже работает (read-api). Нет
 * snapshots endpoint, нет diff. Поэтому version labels на slider — выводятся
 * из schemaVersion текущей версии (single point), а исторические marks из
 * record validFrom timestamps. "Открыть как readonly" просто применяет
 * ?at=ISO в URL — route обрабатывает.
 */

export type TimeTravelModalProps = {
  open: boolean
  onClose: () => void
  detail: DictionaryDetail
  /** Currently applied at-ISO (если активен readonly snapshot). */
  value: string | undefined
  /** Apply the picked moment: opens dict as readonly (route uses ?at=). */
  onApply: (iso: string) => void
  /** Clear at — return к live view. */
  onClear: () => void
  /** Unique record validFrom timestamps (ms) — для slider tick marks. */
  marks: number[]
  /** Total records на текущий момент — для tab counter. */
  totalRecords: number
}

type StepMode = 'version' | 'day'

export function TimeTravelModal({
  open,
  onClose,
  detail,
  value,
  onApply,
  onClear,
  marks,
  totalRecords,
}: TimeTravelModalProps) {
  const { t } = useTranslation()

  const now = Date.now()
  const valueMs = useMemo(() => {
    if (!value) return now
    const ms = Date.parse(value)
    return Number.isNaN(ms) ? now : ms
  }, [value, now])

  const [picked, setPicked] = useState<number>(valueMs)
  const [step, setStep] = useState<StepMode>('version')
  const [tab, setTab] = useState<'changes' | 'records' | 'schema'>('changes')
  /** Open ChangelogDiffModal для конкретной entry — full before/after JSON. */
  const [diffEntryId, setDiffEntryId] = useState<number | undefined>(undefined)

  // Changelog для compare-since-picked: показываем events после picked moment.
  // Размер 200 покрывает типичные dict'ы (5-50 events); cursor pagination
  // можно добавить когда что-то будет с этим больше.
  const changelog = useChangelog(detail.name, 200)
  const eventsSincePicked = useMemo<ChangelogEntry[]>(() => {
    if (!changelog.data) return []
    return changelog.data.entries.filter(
      (e) => new Date(e.publishedAt).getTime() >= picked,
    )
  }, [changelog.data, picked])

  // Find changelog entry which BROUGHT the dict to version active at `picked`:
  // newest entry с publishedAt <= picked. Используется в Schema tab для
  // "история этой версии" + diff.after schema fields.
  const pickedVersionEntry = useMemo<ChangelogEntry | null>(() => {
    if (!changelog.data) return null
    const ordered = [...changelog.data.entries].sort(
      (a, b) => new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime(),
    )
    return ordered.find((e) => new Date(e.publishedAt).getTime() <= picked) ?? null
  }, [changelog.data, picked])

  // Records as-of picked moment — backend findActiveAt(at) на read-api.
  const isAtNow0 = Math.abs(picked - now) < 60_000
  const recordsQuery = useRecords(
    detail.name,
    'PUBLIC,INTERNAL,RESTRICTED',
    // Если picked ≈ now, не передаём `at` — экономим лишний DB hit + cache key.
    isAtNow0 ? undefined : { at: new Date(picked).toISOString() },
  )

  // Range: от min(marks, createdAt) до now+padding.
  const createdMs = Date.parse(detail.createdAt)
  const range = useMemo(() => {
    const minMark = marks.length > 0 ? Math.min(...marks, createdMs) : createdMs - 86_400_000
    const span = now - minMark
    const pad = Math.max(span * 0.04, 86_400_000)
    return { min: minMark - pad, max: now + pad }
  }, [marks, createdMs, now])

  // Sync picked when value prop changes while modal open.
  // (Don't useEffect — пока explicitly value control.)

  const pickedDate = new Date(picked)
  const isAtNow = Math.abs(picked - now) < 60_000 // < 1 min ≈ now
  const targetVersion = detail.schemaVersion // backend doesn't expose snapshots
  const targetDiffers = !isAtNow

  const handlePreset = (deltaMs: number | 'create') => {
    if (deltaMs === 'create') {
      setPicked(createdMs)
    } else if (deltaMs === 0) {
      setPicked(now)
    } else {
      setPicked(now + deltaMs)
    }
  }

  const handleStepNav = (direction: -1 | 1) => {
    if (step === 'day') {
      setPicked((p) => Math.min(range.max, Math.max(range.min, p + direction * 86_400_000)))
    } else {
      // Version step: jump to nearest mark
      const sorted = [...marks].sort((a, b) => a - b)
      if (sorted.length === 0) return
      if (direction === -1) {
        const prev = [...sorted].reverse().find((m) => m < picked - 1000)
        if (prev !== undefined) setPicked(prev)
      } else {
        const next = sorted.find((m) => m > picked + 1000)
        if (next !== undefined) setPicked(next)
      }
    }
  }

  const handleApply = () => {
    if (isAtNow) {
      onClear()
    } else {
      onApply(new Date(picked).toISOString())
    }
    onClose()
  }

  return (
    <Dialog open={open} onOpenChange={(v) => !v && onClose()}>
      <DialogContent size="full" className="!p-0 gap-0 max-h-[95vh]" hideClose>
        {/* Radix a11y: sr-only Title + Description. Visible header — custom layout ниже. */}
        <DialogTitle className="sr-only">
          {t('timeTravel.title', { defaultValue: 'Состояние справочника на момент времени' })}
        </DialogTitle>
        <DialogDescription className="sr-only">
          {t('timeTravel.description', {
            defaultValue:
              'Сравнение текущего состояния справочника с выбранной точкой во времени.',
          })}
        </DialogDescription>
        {/* Header */}
        <div className="flex items-start gap-3 px-6 py-4 border-b border-line">
          <div className="size-8 rounded-md bg-surface-2 inline-flex items-center justify-center shrink-0">
            <ClockCounterClockwiseIcon weight="regular" size={18} className="text-ink-2" />
          </div>
          <div className="min-w-0 flex-1">
            <div className="text-cap text-mute tracking-[0.18em] uppercase">
              Time-travel
              <span className="mx-2 text-line">·</span>
              <span className="text-ink">{detail.name.toUpperCase()}</span>
            </div>
            <h2 className="text-title-md text-ink font-semibold mt-0.5">
              {t('timeTravel.title', { defaultValue: 'Состояние справочника на момент времени' })}
            </h2>
          </div>
          <button
            type="button"
            onClick={onClose}
            aria-label={t('common.close', { defaultValue: 'Закрыть' })}
            className="rounded-sm p-1 text-mute hover:text-ink hover:bg-surface-2 transition-colors"
          >
            <XIcon weight="bold" size={18} />
          </button>
        </div>

        {/* Compare panels */}
        <div className="grid grid-cols-2 border-b border-line">
          <ComparePanel
            label={t('timeTravel.compare.now', { defaultValue: 'Сейчас' })}
            version={detail.schemaVersion}
            ms={now}
            records={totalRecords}
            author={(detail as DictionaryDetail & { updatedBy?: string }).updatedBy}
          />
          <ComparePanel
            label={
              <>
                {t('timeTravel.compare.point', { defaultValue: 'Точка во времени' })}
                {targetDiffers && (
                  <span className="ml-2 text-pink text-cap tracking-wider">
                    · {t('timeTravel.compare.diff', { defaultValue: 'отличается' })}
                  </span>
                )}
              </>
            }
            version={targetVersion}
            ms={picked}
            records={totalRecords /* backend ?at= даст реальный count при заходе */}
            author={(detail as DictionaryDetail & { updatedBy?: string }).updatedBy}
            note={isAtNow ? '«current»' : undefined}
            highlight={targetDiffers}
          />
        </div>

        {/* Quick presets + step toggle */}
        <div className="px-6 py-3 border-b border-line-2 flex items-center gap-3 flex-wrap">
          <div className="text-cap text-mute tracking-[0.16em] uppercase">
            {t('timeTravel.quick', { defaultValue: 'Быстро' })}
          </div>
          <div className="flex items-center gap-2 flex-wrap">
            <Preset label="сейчас" onClick={() => handlePreset(0)} />
            <Preset label="неделя назад" onClick={() => handlePreset(-7 * 86_400_000)} />
            <Preset label="месяц назад" onClick={() => handlePreset(-30 * 86_400_000)} />
            <Preset label="год назад" onClick={() => handlePreset(-365 * 86_400_000)} />
            <Preset label="при создании" onClick={() => handlePreset('create')} />
          </div>
          <div className="ml-auto flex items-center gap-2">
            <div className="text-cap text-mute tracking-[0.16em] uppercase mr-1">
              {t('timeTravel.step', { defaultValue: 'Шаг' })}
            </div>
            <div className="inline-flex rounded-md border border-line bg-surface overflow-hidden">
              <StepBtn active={step === 'version'} onClick={() => setStep('version')} label="версия" />
              <StepBtn active={step === 'day'} onClick={() => setStep('day')} label="день" />
            </div>
            <button
              type="button"
              onClick={() => handleStepNav(-1)}
              className="size-8 rounded-md border border-line bg-surface text-ink-2 hover:bg-surface-2 inline-flex items-center justify-center transition-colors"
              aria-label="prev"
            >
              <CaretLeftIcon weight="bold" size={14} />
            </button>
            <button
              type="button"
              onClick={() => handleStepNav(1)}
              className="size-8 rounded-md border border-line bg-surface text-ink-2 hover:bg-surface-2 inline-flex items-center justify-center transition-colors"
              aria-label="next"
            >
              <CaretRightIcon weight="bold" size={14} />
            </button>
          </div>
        </div>

        {/* Slider */}
        <div className="px-6 pt-5 pb-3 border-b border-line bg-surface-2/40">
          <TimelineSlider
            range={range}
            marks={marks}
            value={picked}
            onChange={setPicked}
            currentVersion={detail.schemaVersion}
          />
        </div>

        {/* Tabs */}
        <div className="px-6 pt-3 border-b border-line">
          <div className="flex items-end gap-6 -mb-px">
            <TabBtn
              active={tab === 'changes'}
              onClick={() => setTab('changes')}
              label={t('timeTravel.tabs.changes', { defaultValue: 'Что изменилось' })}
              count={eventsSincePicked.length}
            />
            <TabBtn
              active={tab === 'records'}
              onClick={() => setTab('records')}
              label={t('timeTravel.tabs.records', { defaultValue: 'Записи на момент' })}
              count={recordsQuery.data?.length ?? totalRecords}
            />
            <TabBtn
              active={tab === 'schema'}
              onClick={() => setTab('schema')}
              label={t('timeTravel.tabs.schema', { defaultValue: 'Структура и поля' })}
            />
          </div>
        </div>

        {/* Tab content */}
        <div className="px-6 py-6 min-h-[180px]">
          {tab === 'changes' && (
            <ChangesTabContent
              isAtNow={isAtNow}
              loading={changelog.isLoading}
              events={eventsSincePicked}
              onOpenDiff={(id) => setDiffEntryId(id)}
            />
          )}
          {tab === 'records' && (
            <RecordsTabContent
              loading={recordsQuery.isLoading}
              records={recordsQuery.data ?? []}
              pickedDate={pickedDate}
            />
          )}
          {tab === 'schema' && (
            <SchemaTabContent
              dictName={detail.name}
              currentSchema={detail.schemaJson}
              currentVersion={detail.schemaVersion}
              pickedEntry={pickedVersionEntry}
            />
          )}
        </div>

        {/* Per-event diff modal — fires lazy fetch /changelog/{id}/diff. */}
        <ChangelogDiffModal
          open={diffEntryId !== undefined}
          onClose={() => setDiffEntryId(undefined)}
          dictionaryName={detail.name}
          entryId={diffEntryId}
        />

        {/* Footer */}
        <div className="px-6 py-3 border-t border-line flex items-center justify-between gap-3 mt-auto">
          <div className="text-cap text-mute tracking-[0.14em] uppercase">
            {t('timeTravel.disclaimer', {
              defaultValue: 'Snapshot не меняет данные — только показывает',
            })}
          </div>
          <div className="flex items-center gap-2">
            <button
              type="button"
              onClick={onClose}
              className="h-9 px-4 rounded-md border border-line bg-surface text-ink-2 hover:bg-surface-2 hover:border-line-2 text-body transition-colors"
            >
              {t('common.close', { defaultValue: 'Закрыть' })}
            </button>
            <button
              type="button"
              onClick={handleApply}
              className="h-9 px-4 rounded-md bg-navy text-on-accent hover:opacity-90 text-body font-semibold transition-opacity"
            >
              {isAtNow
                ? t('timeTravel.applyNow', { defaultValue: 'Закрыть snapshot' })
                : t('timeTravel.apply', {
                    defaultValue: `Открыть v${targetVersion} как readonly`,
                  })}
            </button>
          </div>
        </div>
      </DialogContent>
    </Dialog>
  )
}

function ComparePanel({
  label,
  version,
  ms,
  records,
  author,
  note,
  highlight,
}: {
  label: React.ReactNode
  version: string
  ms: number
  records: number
  author?: string
  note?: string
  highlight?: boolean
}) {
  const d = new Date(ms)
  return (
    <div
      className={cn(
        'px-6 py-4 space-y-1.5 border-r border-line last:border-r-0',
        highlight && 'bg-warn-bg/40',
      )}
    >
      <div className="text-cap text-mute tracking-[0.18em] uppercase">{label}</div>
      <div className="flex items-baseline gap-2 flex-wrap">
        <span className="text-mono text-title-md text-ink font-semibold">v{version}</span>
        <span className="text-mono text-cell text-mute">
          {d.toLocaleDateString(undefined, { dateStyle: 'short' })}
          {' · '}
          {d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })}
        </span>
      </div>
      <div className="text-cell text-ink-2 flex items-center gap-1.5 flex-wrap">
        <span className="tabular-nums">{records}</span>
        <span>{pluralRu(records, 'запись', 'записи', 'записей')}</span>
        {note && (
          <span className="text-mute">
            · <span className="text-mono">{note}</span>
          </span>
        )}
        {author && (
          <span className="text-mute inline-flex items-center gap-1">· <UserCell uuid={author} /></span>
        )}
      </div>
    </div>
  )
}

function Preset({ label, onClick }: { label: string; onClick: () => void }) {
  return (
    <button
      type="button"
      onClick={onClick}
      className="h-8 px-3 rounded-full border border-line bg-surface text-cell text-ink-2 hover:border-ink-2 hover:bg-surface-2 transition-colors"
    >
      {label}
    </button>
  )
}

function StepBtn({
  active,
  onClick,
  label,
}: {
  active: boolean
  onClick: () => void
  label: string
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      aria-pressed={active}
      className={cn(
        'h-8 px-3 text-body border-r border-line last:border-r-0 transition-colors',
        active ? 'bg-navy text-on-accent font-semibold' : 'bg-surface text-ink-2 hover:bg-surface-2',
      )}
    >
      {label}
    </button>
  )
}

function TabBtn({
  active,
  onClick,
  label,
  count,
}: {
  active: boolean
  onClick: () => void
  label: string
  count?: number
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      aria-pressed={active}
      className={cn(
        'pb-2 text-body inline-flex items-center gap-1.5 border-b-2 transition-colors',
        active
          ? 'border-ink text-ink font-semibold'
          : 'border-transparent text-ink-2 hover:text-ink',
      )}
    >
      {label}
      {count !== undefined && (
        <span className={cn('text-mono', active ? 'text-mute' : 'text-mute')}>{count}</span>
      )}
    </button>
  )
}

// ===== Slider =============================================================

function TimelineSlider({
  range,
  marks,
  value,
  onChange,
  currentVersion,
}: {
  range: { min: number; max: number }
  marks: number[]
  value: number
  onChange: (ms: number) => void
  currentVersion: string
}) {
  const span = range.max - range.min || 1
  const pct = ((value - range.min) / span) * 100

  const dateLabel = new Date(value).toLocaleDateString(undefined, {
    month: '2-digit',
    day: '2-digit',
  })

  return (
    <div className="relative w-full">
      {/* Version labels (top) — single current marker для текущей schema version.
        * Когда backend получит snapshots endpoint — здесь будут все версии. */}
      <div className="relative h-5 mb-1">
        <span
          className="absolute -translate-x-1/2 text-mono text-cap text-accent font-semibold"
          style={{ left: '100%' }}
        >
          v{currentVersion}
        </span>
      </div>

      {/* Track + cursor (range input для drag UX) */}
      <div className="relative h-6">
        <div className="absolute inset-x-0 top-1/2 -translate-y-1/2 h-[2px] bg-accent/60 rounded-full" />
        {/* Mark ticks */}
        {marks.map((m) => {
          const p = ((m - range.min) / span) * 100
          if (p < 0 || p > 100) return null
          return (
            <span
              key={m}
              className="absolute top-1/2 -translate-y-1/2 w-px h-3 bg-accent/40"
              style={{ left: `${p}%` }}
            />
          )
        })}
        {/* Cursor */}
        <span
          className="absolute top-1/2 -translate-x-1/2 -translate-y-1/2 size-5 rounded-full bg-ink border-2 border-surface shadow-md pointer-events-none"
          style={{ left: `${pct}%` }}
        />
        {/* Hidden range input для drag */}
        <input
          type="range"
          min={range.min}
          max={range.max}
          step={86_400_000} // day
          value={value}
          onChange={(e) => onChange(Number(e.target.value))}
          className="absolute inset-0 opacity-0 cursor-pointer"
          aria-label="time-travel slider"
        />
      </div>

      {/* Date labels (bottom) — start / value / now.
        * Boundary-метки (min/max) скрываются, когда курсор подъехал близко,
        * иначе текст накладывается в "05.005.05". Порог 12% подобран так,
        * чтобы две DD.MM метки ~ 36px @ text-cap не пересекались на типичной
        * ширине трека ~340px. */}
      <div className="relative h-4 mt-1 text-mono text-cap text-mute">
        {pct > 12 && (
          <span className="absolute left-0">
            {new Date(range.min).toLocaleDateString(undefined, {
              month: '2-digit',
              day: '2-digit',
            })}
          </span>
        )}
        <span
          className="absolute -translate-x-1/2 text-accent font-semibold"
          style={{ left: `${pct}%` }}
        >
          {dateLabel}
        </span>
        {pct < 88 && (
          <span className="absolute right-0">
            {new Date(range.max).toLocaleDateString(undefined, {
              month: '2-digit',
              day: '2-digit',
            })}
          </span>
        )}
      </div>
    </div>
  )
}

function pluralRu(n: number, one: string, few: string, many: string): string {
  const mod10 = n % 10
  const mod100 = n % 100
  if (mod10 === 1 && mod100 !== 11) return one
  if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) return few
  return many
}

/**
 * «Что изменилось» tab — список changelog events которые произошли
 * после picked moment. Каждый event с kind-aware icon/badge + summary
 * counts + кнопка «Diff» открывает {@link ChangelogDiffModal} с full
 * before/after JSON.
 *
 * <p>isAtNow={true} → empty state «нет изменений». isLoading → skeleton.
 * events=[] → «между этими версиями нет изменений».
 */
function ChangesTabContent({
  isAtNow,
  loading,
  events,
  onOpenDiff,
}: {
  isAtNow: boolean
  loading: boolean
  events: ChangelogEntry[]
  onOpenDiff: (id: number) => void
}) {
  const { t } = useTranslation()

  if (isAtNow) {
    return (
      <div className="flex items-center justify-center text-mute text-body">
        {t('timeTravel.empty.same', {
          defaultValue: 'Между этими версиями нет изменений.',
        })}
      </div>
    )
  }
  if (loading) {
    return (
      <div className="space-y-2">
        {[1, 2, 3].map((i) => (
          <div
            key={i}
            className="h-12 rounded-lg border border-line bg-surface-2/40 animate-pulse"
          />
        ))}
      </div>
    )
  }
  if (events.length === 0) {
    return (
      <div className="flex items-center justify-center text-mute text-body">
        {t('timeTravel.empty.noEvents', {
          defaultValue: 'Между этими моментами ничего не менялось.',
        })}
      </div>
    )
  }

  return (
    <ul className="space-y-2">
      {events.map((e) => {
        const Icon = kindIcon(e.kind)
        const added = e.diff.added.length
        const changed = e.diff.changed.length
        const removed = e.diff.removed.length
        return (
          <li
            key={e.id}
            className="rounded-lg border border-line bg-surface p-3 flex items-start gap-3"
          >
            <Icon weight="duotone" size={20} className="text-mute shrink-0 mt-0.5" />
            <div className="flex-1 min-w-0">
              <div className="flex items-baseline gap-2 flex-wrap">
                {e.version && (
                  <span className="text-mono text-cell text-ink font-semibold tabular-nums">
                    v{e.version}
                  </span>
                )}
                <Badge variant={kindBadgeVariant(e.kind)}>{kindLabel(e.kind, t)}</Badge>
                <span className="text-mono text-cap text-mute tabular-nums ml-auto">
                  {added > 0 && <span className="text-aurora">+{added}</span>}
                  {added > 0 && (changed > 0 || removed > 0) && ' '}
                  {changed > 0 && <span className="text-warn">~{changed}</span>}
                  {changed > 0 && removed > 0 && ' '}
                  {removed > 0 && <span className="text-danger">−{removed}</span>}
                </span>
              </div>
              {e.headers && (
                <div className="text-body text-ink mt-0.5 truncate">{e.headers}</div>
              )}
              <div className="flex items-center gap-3 mt-1 text-cell text-mute">
                <span className="tabular-nums">
                  {new Date(e.publishedAt).toLocaleString()}
                </span>
                <UserCell uuid={e.publishedBy.sub} />
                <button
                  type="button"
                  onClick={() => onOpenDiff(e.id)}
                  className="ml-auto text-accent hover:underline"
                >
                  {t('timeTravel.action.diff', { defaultValue: 'Diff' })} →
                </button>
              </div>
            </div>
          </li>
        )
      })}
    </ul>
  )
}

/**
 * «Записи на момент» tab — table of records as-of picked moment.
 * Uses {@code useRecords(at: pickedISO)} — backend findActiveAt(at).
 *
 * <p>Top 50 rendered. Если больше — показываем counter с pagination
 * notice (full pagination не нужен в snapshot view).
 */
function RecordsTabContent({
  loading,
  records,
  pickedDate,
}: {
  loading: boolean
  records: FlattenedRecord[]
  pickedDate: Date
}) {
  const { t } = useTranslation()

  if (loading) {
    return (
      <div className="space-y-1">
        {[1, 2, 3, 4, 5].map((i) => (
          <div
            key={i}
            className="h-9 rounded-md border border-line bg-surface-2/40 animate-pulse"
          />
        ))}
      </div>
    )
  }
  if (records.length === 0) {
    return (
      <div className="flex items-center justify-center text-mute text-body py-10">
        {t('timeTravel.records.empty', {
          date: pickedDate.toLocaleDateString(),
          defaultValue: `На ${pickedDate.toLocaleDateString()} записей не было.`,
        })}
      </div>
    )
  }

  const visible = records.slice(0, 50)
  const truncated = records.length > 50

  return (
    <div className="space-y-2">
      <div className="text-cap text-mute uppercase tracking-wider">
        {t('timeTravel.records.title', {
          count: records.length,
          defaultValue: `${records.length} записей на ${pickedDate.toLocaleDateString()}`,
        })}
      </div>
      <div className="rounded-md border border-line overflow-hidden">
        <table className="w-full text-cell">
          <thead className="bg-surface-2 text-cap text-mute uppercase tracking-wider">
            <tr>
              <th className="text-left px-3 py-2">businessKey</th>
              <th className="text-left px-3 py-2">
                {t('timeTravel.records.col.name', { defaultValue: 'Название' })}
              </th>
              <th className="text-left px-3 py-2">scope</th>
              <th className="text-left px-3 py-2">
                {t('timeTravel.records.col.validFrom', { defaultValue: 'Действует с' })}
              </th>
            </tr>
          </thead>
          <tbody>
            {visible.map((r) => {
              const displayName =
                (r.data['name_ru'] as string | undefined) ??
                (r.data['name'] as string | undefined) ??
                (r.data['name_en'] as string | undefined) ??
                '—'
              return (
                <tr key={r.id} className="border-t border-line">
                  <td className="px-3 py-2 font-mono">{r.businessKey}</td>
                  <td className="px-3 py-2 text-ink truncate max-w-[300px]">{displayName}</td>
                  <td className="px-3 py-2 text-cap">{r.dataScope}</td>
                  <td className="px-3 py-2 tabular-nums text-mute">
                    {new Date(r.validFrom).toLocaleDateString()}
                  </td>
                </tr>
              )
            })}
          </tbody>
        </table>
      </div>
      {truncated && (
        <div className="text-cap text-mute italic">
          {t('timeTravel.records.truncated', {
            visible: visible.length,
            total: records.length,
            defaultValue: `Показано ${visible.length} из ${records.length}. Открыть как readonly для полного списка.`,
          })}
        </div>
      )}
    </div>
  )
}

/**
 * «Структура и поля» tab — fields list at picked version + history panel
 * на правой колонке.
 *
 * <p>Поля fetch'аются через ChangelogDiff endpoint (after schema). Если
 * pickedEntry null — fallback на current schemaJson.
 *
 * <p>Right panel показывает changelog entry который attached к picked
 * version: commit (audit id), автор, дата, headers/message.
 */
function SchemaTabContent({
  dictName,
  currentSchema,
  currentVersion,
  pickedEntry,
}: {
  dictName: string
  currentSchema: { properties?: Record<string, unknown> }
  currentVersion: string
  pickedEntry: ChangelogEntry | null
}) {
  const { t } = useTranslation()
  // Fetch full diff для picked entry чтобы получить historical `after`
  // schema. Если pickedEntry — current/latest, after === current; иначе
  // berunaround older snapshot.
  const diff = useChangelogDiff(dictName, pickedEntry?.id)
  const after = (diff.data?.after ?? null) as
    | { properties?: Record<string, unknown> }
    | null

  // Fallback chain: diff.after → currentSchema. Эта семантика:
  // "если для версии диффа нет → показываем live schema with disclaimer".
  const propsSource = after ?? currentSchema
  const fields = Object.keys(propsSource.properties ?? {}).sort()
  const versionLabel = pickedEntry?.version ?? currentVersion
  const isCurrentSchemaFallback = !after && Boolean(pickedEntry)

  return (
    <div className="grid grid-cols-1 md:grid-cols-[1fr_minmax(220px,300px)] gap-6">
      {/* LEFT — fields list */}
      <div className="space-y-2">
        <div className="text-cap text-mute uppercase tracking-wider">
          {t('timeTravel.schema.fieldsTitle', {
            version: versionLabel,
            defaultValue: `Поля справочника на v${versionLabel}`,
          })}
        </div>
        {diff.isLoading && (
          <div className="space-y-1">
            {[1, 2, 3, 4, 5].map((i) => (
              <div
                key={i}
                className="h-8 rounded-md border border-line bg-surface-2/40 animate-pulse"
              />
            ))}
          </div>
        )}
        {!diff.isLoading && fields.length === 0 && (
          <div className="text-cell text-mute py-4 text-center">
            {t('timeTravel.schema.empty', { defaultValue: 'Полей нет.' })}
          </div>
        )}
        {!diff.isLoading && fields.length > 0 && (
          <ul className="rounded-md border border-line divide-y divide-line">
            {fields.map((f) => (
              <li key={f} className="px-3 py-2 font-mono text-cell text-ink">
                {f}
              </li>
            ))}
          </ul>
        )}
        {isCurrentSchemaFallback && (
          <div className="text-cap text-mute italic">
            {t('timeTravel.schema.fallback', {
              defaultValue: 'Показана live-схема — diff на эту версию недоступен.',
            })}
          </div>
        )}
      </div>

      {/* RIGHT — history panel */}
      <div className="space-y-2">
        <div className="text-cap text-mute uppercase tracking-wider">
          {t('timeTravel.schema.historyTitle', {
            defaultValue: 'История этой версии',
          })}
        </div>
        {pickedEntry ? (
          <dl className="rounded-md border border-line bg-surface p-3 space-y-2 text-cell">
            <div className="grid grid-cols-[auto_1fr] gap-x-2 gap-y-1">
              <dt className="text-cap text-mute uppercase tracking-wider">commit</dt>
              <dd className="font-mono text-accent">#{pickedEntry.id}</dd>
              <dt className="text-cap text-mute uppercase tracking-wider">
                {t('timeTravel.schema.author', { defaultValue: 'автор' })}
              </dt>
              <dd><UserCell uuid={pickedEntry.publishedBy.sub} /></dd>
              <dt className="text-cap text-mute uppercase tracking-wider">
                {t('timeTravel.schema.date', { defaultValue: 'дата' })}
              </dt>
              <dd className="tabular-nums">
                {new Date(pickedEntry.publishedAt).toLocaleString()}
              </dd>
            </div>
            {pickedEntry.headers && (
              <div className="text-body text-ink mt-2 italic">
                «{pickedEntry.headers}»
              </div>
            )}
          </dl>
        ) : (
          <div className="text-cell text-mute italic">
            {t('timeTravel.schema.noHistory', {
              defaultValue: 'История недоступна для выбранного момента.',
            })}
          </div>
        )}
      </div>
    </div>
  )
}
