import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/ui'
import type { DictionaryDetail, FlattenedRecord } from '@/api/client'
import { cn } from '@/lib/utils'

/**
 * ExportModal — per redesign prototype (handoff line 1059-1092).
 *
 * <p>Layout сверху-вниз:
 * <ul>
 *   <li>Header: ЭКСПОРТ · {NAME} V{version}</li>
 *   <li>Формат — 4 card buttons (CSV/JSON/Excel/SQL)</li>
 *   <li>Что выгружаем — pill toggle (всё / отфильтрованное / выделенное)</li>
 *   <li>Колонки (N) — chip toggle list из schema.properties</li>
 *   <li>Кодировка + Разделитель — для CSV (другие форматы скрывают)</li>
 *   <li>Preview filename + estimated rows/cols footer caption</li>
 *   <li>Действия: Отмена / Скачать</li>
 * </ul>
 *
 * <p><b>Backend coverage:</b>
 * <ul>
 *   <li>CSV — backend `POST /dictionaries/{name}/records/export-selected.csv`
 *       (передаём businessKeys). Backend сейчас не принимает column subset —
 *       UI показывает выбранные колонки только cosmetically; backend вернёт
 *       все публичные поля. TODO когда backend поддержит `?columns=` param.</li>
 *   <li>JSON — client-side build из filteredRecords/selection (быстро, без
 *       network round-trip). Поддерживает column subset правильно.</li>
 *   <li>Excel / SQL — backend endpoint не реализован; disabled с tooltip
 *       "Скоро · backend export job".</li>
 * </ul>
 */

export type ExportFormat = 'csv' | 'json' | 'xlsx' | 'sql'
export type ExportScope = 'all' | 'filtered' | 'selected'

type ExportModalProps = {
  open: boolean
  onClose: () => void
  detail: DictionaryDetail
  totalCount: number
  filteredRecords: FlattenedRecord[]
  selection: Set<string>
  /** Backend CSV export — uses POST /export-selected.csv с businessKeys. */
  onExportCsv: (businessKeys: string[]) => void
  exportPending: boolean
}

const FORMATS: { id: ExportFormat; label: string; ext: string; disabled?: boolean; reason?: string }[] = [
  { id: 'csv', label: 'CSV', ext: '.csv' },
  { id: 'json', label: 'JSON', ext: '.json' },
  { id: 'xlsx', label: 'Excel', ext: '.xlsx', disabled: true, reason: 'Скоро · backend export job' },
  { id: 'sql', label: 'SQL', ext: '.sql', disabled: true, reason: 'Скоро · backend export job' },
]

const ENCODINGS = [
  { id: 'utf-8', label: 'utf-8' },
  { id: 'win1251', label: 'windows-1251' },
] as const

const DELIMITERS = [
  { id: ',', label: 'запятая ,' },
  { id: ';', label: 'точка с запятой ;' },
  { id: '\t', label: 'табуляция' },
] as const

type Encoding = (typeof ENCODINGS)[number]['id']
type Delimiter = (typeof DELIMITERS)[number]['id']

export function ExportModal({
  open,
  onClose,
  detail,
  totalCount,
  filteredRecords,
  selection,
  onExportCsv,
  exportPending,
}: ExportModalProps) {
  const { t } = useTranslation()

  const allColumns = useMemo(
    () => Object.keys(detail.schemaJson.properties ?? {}),
    [detail.schemaJson.properties],
  )

  const [format, setFormat] = useState<ExportFormat>('csv')
  // Default scope: selected > filtered > all (как пользователь хочет реально).
  const initialScope: ExportScope =
    selection.size > 0 ? 'selected' : filteredRecords.length < totalCount ? 'filtered' : 'all'
  const [scope, setScope] = useState<ExportScope>(initialScope)
  const [columns, setColumns] = useState<Set<string>>(() => {
    // По умолчанию включаем "ключевые" колонки: id, name*, locale-aware. Если
    // у схемы больше 6 — disable некоторые heavy (raw_json, _instruments).
    const initial = new Set<string>(allColumns)
    return initial
  })
  const [encoding, setEncoding] = useState<Encoding>('utf-8')
  const [delimiter, setDelimiter] = useState<Delimiter>(',')

  const toggleColumn = (name: string) => {
    setColumns((prev) => {
      const next = new Set(prev)
      if (next.has(name)) next.delete(name)
      else next.add(name)
      return next
    })
  }

  const selectedCount = selection.size
  const filteredCount = filteredRecords.length

  // Estimated row count для текущего scope.
  const estimatedRows =
    scope === 'all' ? totalCount : scope === 'filtered' ? filteredCount : selectedCount

  // Preview filename per prototype: name_vX_filtered.ext / _selected / _all
  const previewName = useMemo(() => {
    const suffix =
      scope === 'all' ? '' : scope === 'filtered' ? '_filtered' : '_selected'
    return `${detail.name}_v${detail.schemaVersion}${suffix}${FORMATS.find((f) => f.id === format)?.ext}`
  }, [detail.name, detail.schemaVersion, scope, format])

  const canExport =
    !exportPending && columns.size > 0 && estimatedRows > 0 && !FORMATS.find((f) => f.id === format)?.disabled

  const handleDownload = () => {
    // Resolve businessKeys для текущего scope.
    const keys =
      scope === 'all'
        ? filteredRecords.map((r) => r.businessKey)
        : scope === 'filtered'
          ? filteredRecords.map((r) => r.businessKey)
          : Array.from(selection)

    if (keys.length === 0) return

    if (format === 'csv') {
      onExportCsv(keys)
      onClose()
      return
    }

    if (format === 'json') {
      // Client-side JSON build. Берём filteredRecords (для selected — фильтруем).
      const source =
        scope === 'selected'
          ? filteredRecords.filter((r) => selection.has(r.businessKey))
          : filteredRecords
      const rows = source.map((r) => {
        const filtered: Record<string, unknown> = {}
        for (const col of columns) {
          if (col in r.data) filtered[col] = r.data[col]
        }
        return {
          id: r.id,
          businessKey: r.businessKey,
          dataScope: r.dataScope,
          validFrom: r.validFrom,
          validTo: r.validTo,
          data: filtered,
        }
      })
      const blob = new Blob([JSON.stringify(rows, null, 2)], {
        type: 'application/json',
      })
      triggerDownload(blob, previewName)
      onClose()
      return
    }

    // xlsx/sql — disabled в UI, но guard на всякий.
  }

  return (
    <Dialog open={open} onOpenChange={(v) => !v && onClose()}>
      <DialogContent size="xl" className="!p-0 gap-0">
        {/* Radix a11y: visually-hidden Title + Description. Visible heading
          * рендерится ниже как кастомный layout. */}
        <DialogTitle className="sr-only">
          {t('export.title', { defaultValue: 'Экспорт' })} — {detail.name}
        </DialogTitle>
        <DialogDescription className="sr-only">
          {t('export.description', {
            defaultValue: 'Выбор формата, объёма и колонок для выгрузки.',
          })}
        </DialogDescription>
        {/* Header */}
        <div className="px-6 py-4 border-b border-line">
          <div className="text-cap text-mute tracking-[0.18em] uppercase">
            {t('export.title', { defaultValue: 'Экспорт' })}
            <span className="mx-2 text-line">·</span>
            <span className="text-ink">{detail.name.toUpperCase()}</span>
            <span className="mx-1">v{detail.schemaVersion}</span>
          </div>
        </div>

        {/* Body */}
        <div className="px-6 py-5 space-y-5">
          {/* Format cards */}
          <Section label={t('export.format', { defaultValue: 'Формат' })}>
            <div className="grid grid-cols-4 gap-2">
              {FORMATS.map((f) => (
                <button
                  key={f.id}
                  type="button"
                  onClick={() => !f.disabled && setFormat(f.id)}
                  disabled={f.disabled}
                  title={f.reason}
                  className={cn(
                    'flex flex-col items-center justify-center gap-0.5 h-[68px] rounded-md border transition-colors',
                    format === f.id
                      ? 'border-ink bg-surface text-ink ring-1 ring-ink'
                      : 'border-line bg-surface text-ink-2 hover:border-line-2 hover:bg-surface-2',
                    f.disabled && 'opacity-40 cursor-not-allowed hover:bg-surface hover:border-line',
                  )}
                >
                  <span className="text-title-md font-semibold leading-none">{f.label}</span>
                  <span className="text-mono text-mute leading-none">{f.ext}</span>
                </button>
              ))}
            </div>
          </Section>

          {/* Scope toggle */}
          <Section label={t('export.scope', { defaultValue: 'Что выгружаем' })}>
            <div className="inline-flex rounded-md border border-line bg-surface overflow-hidden">
              <ScopeBtn
                active={scope === 'all'}
                onClick={() => setScope('all')}
                label={t('export.scope.all', { defaultValue: 'всё' })}
                count={totalCount}
              />
              <ScopeBtn
                active={scope === 'filtered'}
                onClick={() => setScope('filtered')}
                label={t('export.scope.filtered', { defaultValue: 'отфильтрованное' })}
                count={filteredCount}
                disabled={filteredCount === totalCount}
              />
              <ScopeBtn
                active={scope === 'selected'}
                onClick={() => setScope('selected')}
                label={t('export.scope.selected', { defaultValue: 'выделенное' })}
                count={selectedCount}
                disabled={selectedCount === 0}
              />
            </div>
          </Section>

          {/* Columns toggle chips */}
          <Section
            label={
              <>
                {t('export.columns', { defaultValue: 'Колонки' })}
                <span className="ml-1 text-mono text-mute normal-case">
                  ({columns.size}/{allColumns.length})
                </span>
              </>
            }
          >
            <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-1.5">
              {allColumns.map((col) => {
                const active = columns.has(col)
                return (
                  <button
                    key={col}
                    type="button"
                    onClick={() => toggleColumn(col)}
                    aria-pressed={active}
                    className={cn(
                      'h-8 px-3 rounded-md border text-mono text-body text-left flex items-center gap-1.5 transition-colors',
                      active
                        ? 'border-accent bg-accent-bg text-accent'
                        : 'border-line bg-surface text-ink-2 hover:border-line-2 hover:bg-surface-2',
                    )}
                  >
                    <span className="shrink-0 opacity-80">{active ? '✓' : ''}</span>
                    <span className="truncate">{col}</span>
                  </button>
                )
              })}
            </div>
          </Section>

          {/* CSV-only: encoding + delimiter */}
          {format === 'csv' && (
            <div className="grid grid-cols-2 gap-4">
              <Section label={t('export.encoding', { defaultValue: 'Кодировка' })}>
                <Select
                  value={encoding}
                  onChange={(v) => setEncoding(v as Encoding)}
                  options={ENCODINGS.map((e) => ({ value: e.id, label: e.label }))}
                />
              </Section>
              <Section label={t('export.delimiter', { defaultValue: 'Разделитель' })}>
                <Select
                  value={delimiter}
                  onChange={(v) => setDelimiter(v as Delimiter)}
                  options={DELIMITERS.map((d) => ({ value: d.id, label: d.label }))}
                />
              </Section>
            </div>
          )}
        </div>

        {/* Preview footer */}
        <div className="px-6 py-3 border-t border-line-2 bg-surface-2/50 flex items-center justify-between flex-wrap gap-2">
          <div className="text-cell text-mute">
            <span className="text-cap tracking-[0.16em] uppercase mr-2">
              {t('export.preview.filename', { defaultValue: 'Превью имени файла' })}
            </span>
            <span className="text-mono text-accent">{previewName}</span>
          </div>
          <div className="text-cell text-mute tabular-nums">
            ≈ {estimatedRows} {pluralRu(estimatedRows, 'строка', 'строки', 'строк')}{' '}
            · {columns.size} {pluralRu(columns.size, 'колонка', 'колонки', 'колонок')}
          </div>
        </div>

        {/* Footer actions */}
        <div className="px-6 py-3 border-t border-line flex items-center justify-between gap-3">
          <div className="text-cap text-mute tracking-[0.14em] uppercase">
            {t('export.disclaimer', {
              defaultValue: 'Включает только публичные поля · readonly 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.cancel', { defaultValue: 'Отмена' })}
            </button>
            <button
              type="button"
              onClick={handleDownload}
              disabled={!canExport}
              className="h-9 px-4 rounded-md bg-navy text-on-accent hover:opacity-90 disabled:opacity-40 disabled:cursor-not-allowed text-body font-semibold transition-opacity"
            >
              {exportPending
                ? t('export.pending', { defaultValue: 'Экспорт…' })
                : t('export.download', { defaultValue: 'Скачать' })}
            </button>
          </div>
        </div>
      </DialogContent>
    </Dialog>
  )
}

function Section({
  label,
  children,
}: {
  label: React.ReactNode
  children: React.ReactNode
}) {
  return (
    <div className="space-y-2">
      <div className="text-cap text-mute tracking-[0.16em] uppercase">{label}</div>
      {children}
    </div>
  )
}

function ScopeBtn({
  active,
  onClick,
  label,
  count,
  disabled,
}: {
  active: boolean
  onClick: () => void
  label: string
  count: number
  disabled?: boolean
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      aria-pressed={active}
      className={cn(
        'h-9 px-4 text-body transition-colors border-r border-line last:border-r-0',
        active
          ? 'bg-navy text-on-accent font-semibold'
          : 'bg-surface text-ink-2 hover:bg-surface-2',
        disabled && 'opacity-40 cursor-not-allowed hover:bg-surface',
      )}
    >
      {label}
      <span className={cn('ml-1 tabular-nums', active ? 'opacity-90' : 'text-mute')}>
        ({count})
      </span>
    </button>
  )
}

function Select<T extends string>({
  value,
  onChange,
  options,
}: {
  value: T
  onChange: (v: T) => void
  options: { value: T; label: string }[]
}) {
  return (
    <select
      value={value}
      onChange={(e) => onChange(e.target.value as T)}
      className="h-9 w-full px-3 rounded-md border border-line bg-surface text-ink text-body hover:border-line-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent"
    >
      {options.map((o) => (
        <option key={o.value} value={o.value}>
          {o.label}
        </option>
      ))}
    </select>
  )
}

/** Trigger browser download через temporary blob URL + <a download>. */
function triggerDownload(blob: Blob, filename: string) {
  const url = window.URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = filename
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
  requestAnimationFrame(() => window.URL.revokeObjectURL(url))
}

/** Russian plural form helper (1 строка / 2-4 строки / 5+ строк). */
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
}
