import { useState, useEffect, useCallback, useRef } from 'react'
import * as XLSX from 'xlsx'
import {
  KeyRound, Save, RefreshCw, Check, X, AlertCircle,
  ChevronDown, ChevronRight, Trash2, Plus, Eye, EyeOff,
  Lock, Unlock, Info, Zap, Upload, Globe, Pencil,
} from 'lucide-react'
import { api, type TTLockConfig, type RoomLockMapping, type Workstation } from '../lib/api'
import { useAuth } from '../contexts/AuthContext'
import { cn } from '../lib/utils'
import type { Room } from '../types'

// ── Helpers ───────────────────────────────────────────────────────────────────

function Section({ title, children, defaultOpen = true }: {
  title: string; children: React.ReactNode; defaultOpen?: boolean
}) {
  const [open, setOpen] = useState(defaultOpen)
  return (
    <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
      <button
        onClick={() => setOpen(o => !o)}
        className="w-full flex items-center justify-between px-6 py-4 bg-slate-50 dark:bg-slate-800/50 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors text-left"
      >
        <span className="font-semibold text-slate-800 dark:text-slate-200">{title}</span>
        {open
          ? <ChevronDown size={16} className="text-slate-500" />
          : <ChevronRight size={16} className="text-slate-500" />}
      </button>
      {open && <div className="p-6 bg-white dark:bg-slate-900">{children}</div>}
    </div>
  )
}

function PasswordInput({ value, onChange, placeholder }: {
  value: string; onChange: (v: string) => void; placeholder?: string
}) {
  const [show, setShow] = useState(false)
  return (
    <div className="relative">
      <input
        type={show ? 'text' : 'password'}
        value={value}
        onChange={e => onChange(e.target.value)}
        placeholder={placeholder}
        className="input pr-10 w-full"
      />
      <button
        type="button"
        onClick={() => setShow(s => !s)}
        className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600"
      >
        {show ? <EyeOff size={15} /> : <Eye size={15} />}
      </button>
    </div>
  )
}

// ── Hint box ──────────────────────────────────────────────────────────────────
function Hint({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex gap-2 p-3 rounded-lg bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300 text-xs">
      <Info size={14} className="shrink-0 mt-0.5" />
      <div>{children}</div>
    </div>
  )
}

// ── Sector Picker — 2 rows of 8 ───────────────────────────────────────────────

function SectorPicker({ value, onChange }: {
  value: string
  onChange: (v: string) => void
}) {
  const selected = new Set(
    value.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n) && n >= 1 && n <= 16)
  )

  const toggle = (n: number) => {
    const next = new Set(selected)
    if (next.has(n)) { next.delete(n) } else { next.add(n) }
    onChange(Array.from(next).sort((a, b) => a - b).join(','))
  }

  return (
    <div>
      <div className="grid grid-cols-8 gap-2 mt-1">
        {Array.from({ length: 16 }, (_, i) => i + 1).map(n => (
          <button
            key={n}
            type="button"
            onClick={() => toggle(n)}
            title={`Сектор ${n}`}
            className={cn(
              'w-9 h-9 rounded-full text-sm font-semibold transition-colors border-2',
              selected.has(n)
                ? 'bg-brand-600 border-brand-600 text-white hover:bg-brand-700 hover:border-brand-700'
                : 'bg-white dark:bg-slate-800 border-slate-300 dark:border-slate-600 text-slate-500 dark:text-slate-400 hover:border-brand-400 hover:text-brand-600',
            )}
          >
            {n}
          </button>
        ))}
      </div>
      <p className="text-xs text-slate-500 mt-2">
        Выбрано: <code className="bg-slate-100 dark:bg-slate-800 px-1 rounded font-mono">{value || '(нет)'}</code>
        {' — '}секторы Mifare-карты, которые использует TTHotel.
      </p>
    </div>
  )
}

// ── Toast ─────────────────────────────────────────────────────────────────────

interface ToastItem { id: number; type: 'success' | 'error'; message: string }

function ToastContainer({ toasts, onRemove }: { toasts: ToastItem[]; onRemove: (id: number) => void }) {
  return (
    <div className="fixed top-5 left-1/2 -translate-x-1/2 z-50 flex flex-col gap-2 pointer-events-none" style={{ minWidth: 320 }}>
      {toasts.map(t => (
        <div
          key={t.id}
          className={cn(
            'flex items-center gap-3 px-4 py-3 rounded-xl shadow-lg text-sm font-medium pointer-events-auto',
            'animate-in fade-in slide-in-from-top-2 duration-200',
            t.type === 'success'
              ? 'bg-emerald-600 text-white'
              : 'bg-red-600 text-white',
          )}
        >
          {t.type === 'success'
            ? <Check size={16} className="shrink-0" />
            : <AlertCircle size={16} className="shrink-0" />}
          <span className="flex-1">{t.message}</span>
          <button onClick={() => onRemove(t.id)} className="opacity-70 hover:opacity-100">
            <X size={14} />
          </button>
        </div>
      ))}
    </div>
  )
}

// ── DLL error note ────────────────────────────────────────────────────────────
function isDllEntryError(msg: string): boolean {
  return msg.includes('точку входа') || msg.toLowerCase().includes('entrypoint') || msg.toLowerCase().includes('entry point')
}

// ── Main Page ─────────────────────────────────────────────────────────────────

export function TTLockPage() {
  const { user } = useAuth()
  const slug = user?.hotelSlug ?? ''

  const [config, setConfig]         = useState<TTLockConfig | null>(null)
  const [mappings, setMappings]     = useState<RoomLockMapping[]>([])
  const [rooms, setRooms]           = useState<Room[]>([])
  const [workstations, setWorkstations] = useState<Workstation[]>([])
  const [loading, setLoading]       = useState(true)
  const [saving, setSaving]         = useState(false)
  const [testingApi, setTestingApi] = useState(false)

  // Toast notifications
  const [toasts, setToasts]         = useState<ToastItem[]>([])
  const toastCounter                = useRef(0)

  const showToast = useCallback((type: 'success' | 'error', message: string) => {
    const id = ++toastCounter.current
    setToasts(prev => [...prev, { id, type, message }])
    setTimeout(() => setToasts(prev => prev.filter(t => t.id !== id)), 4000)
  }, [])

  const removeToast = useCallback((id: number) => {
    setToasts(prev => prev.filter(t => t.id !== id))
  }, [])

  // Форма настроек
  const [isEnabled, setIsEnabled]       = useState(false)
  const [clientId, setClientId]         = useState('')
  const [clientSecret, setClientSecret] = useState('')
  const [cardSectors, setCardSectors]   = useState('1,2,3,4,5,6,7,8,9,10')

  // Per-workstation COM port state
  const [wsComPorts, setWsComPorts]   = useState<Record<string, string>>({})
  const [wsSaving, setWsSaving]       = useState<Record<string, boolean>>({})
  const [wsTesting, setWsTesting]     = useState<Record<string, boolean>>({})

  // Per-workstation port list state
  const [wsPorts, setWsPorts]               = useState<Record<string, { port: string; description?: string }[]>>({})
  const [wsPortsLoading, setWsPortsLoading] = useState<Record<string, boolean>>({})

  // Форма добавления привязки
  const [addRoomId, setAddRoomId]     = useState('')
  const [addLockMac, setAddLockMac]   = useState('')
  const [addLockName, setAddLockName] = useState('')
  const [addLoading, setAddLoading]   = useState(false)

  // Редактирование привязки
  const [editingMac, setEditingMac]     = useState<string | null>(null)
  const [editRoomId, setEditRoomId]     = useState('')
  const [editLockMac, setEditLockMac]   = useState('')
  const [editLockName, setEditLockName] = useState('')
  const [editLoading, setEditLoading]   = useState(false)

  // Импорт из XLS
  interface ImportRow {
    doorName: string
    lockMac:  string
    roomId:   string
    skip:     boolean
  }
  const [importRows, setImportRows] = useState<ImportRow[] | null>(null)
  const [importing, setImporting]   = useState(false)
  const importFileRef = useRef<HTMLInputElement>(null)

  const handleImportFile = async (file: File) => {
    try {
      const buf  = await file.arrayBuffer()
      const wb   = XLSX.read(buf, { type: 'array' })
      const ws   = wb.Sheets[wb.SheetNames[0]]
      const rows = XLSX.utils.sheet_to_json(ws, { defval: '' }) as Array<Record<string, unknown>>

      const parsed: ImportRow[] = rows.map(row => {
        const doorName      = String(row['Door Name'] ?? '').trim()
        const rawMac        = String(row['Lock Mac']  ?? '').trim()
        const mac           = rawMac.replace(/:/g, '').toUpperCase()
        const matched       = rooms.find(r => String(r.number).trim() === doorName)
        const alreadyMapped = matched ? mappings.some(m => m.roomId === matched.id) : false
        return { doorName, lockMac: mac, roomId: matched?.id ?? '', skip: alreadyMapped }
      }).filter(r => r.lockMac.length > 0)

      setImportRows(parsed)
    } catch {
      showToast('error', 'Не удалось прочитать файл. Убедитесь, что это .xls/.xlsx из TTHotel.')
    }
  }

  const handleImport = async () => {
    if (!importRows) return
    setImporting(true)
    let ok = 0, fail = 0
    await Promise.allSettled(
      importRows
        .filter(r => !r.skip && r.lockMac)
        .map(async r => {
          try {
            await api.ttlock.mapRoom(slug, {
              roomId:   r.roomId || undefined as any,
              lockMac:  r.lockMac,
              lockName: r.doorName || undefined,
            })
            ok++
          } catch { fail++ }
        })
    )
    setImporting(false)
    setImportRows(null)
    await load()
    showToast('success', `Импортировано: ${ok} замков${fail ? `, ошибок: ${fail}` : ''}`)
  }

  const load = useCallback(async () => {
    if (!slug) return
    setLoading(true)
    try {
      const [cfg, maps, roomList, wsList] = await Promise.all([
        api.ttlock.getConfig(slug),
        api.ttlock.getRoomLocks(slug),
        api.rooms.list(slug),
        api.workstations.list(slug),
      ])
      setConfig(cfg)
      setIsEnabled(cfg.isEnabled)
      setClientId(cfg.clientId)
      setCardSectors(cfg.cardSectors || '1,2,3,4,5,6,7,8,9,10')
      setMappings(maps)
      setRooms(roomList)
      setWorkstations(wsList)
      setWsComPorts(Object.fromEntries(wsList.map(w => [w.id, w.ttlockComPort ?? ''])))
    } catch {
      showToast('error', 'Не удалось загрузить настройки')
    } finally {
      setLoading(false)
    }
  }, [slug])

  useEffect(() => { load() }, [load])

  const handleSave = async () => {
    setSaving(true)
    try {
      await api.ttlock.updateConfig(slug, {
        isEnabled,
        clientId:     clientId.trim(),
        clientSecret: clientSecret.trim() || undefined,
        cardSectors:  cardSectors.trim(),
        apiServer:    'https://euapi.ttlock.com',
      })
      showToast('success', 'Настройки сохранены')
      setClientSecret('')
      await load()
    } catch {
      showToast('error', 'Ошибка сохранения')
    } finally {
      setSaving(false)
    }
  }

  const handleTestApi = async () => {
    const ws = workstations[0]
    if (!ws) { showToast('error', 'Нет рабочих мест — добавьте на странице «Оборудование»'); return }
    setTestingApi(true)
    try {
      await api.ttlock.testApi(slug, ws.id)
      showToast('success', 'Подключение к TTLock Cloud API успешно')
    } catch (e) {
      showToast('error', e instanceof Error ? e.message : 'Ошибка проверки API')
    } finally {
      setTestingApi(false)
    }
  }

  const handleAddMapping = async () => {
    if (!addRoomId || !addLockMac.trim()) return
    setAddLoading(true)
    try {
      await api.ttlock.mapRoom(slug, {
        roomId:   addRoomId,
        lockMac:  addLockMac.trim(),
        lockName: addLockName.trim() || undefined,
      })
      setAddRoomId('')
      setAddLockMac('')
      setAddLockName('')
      await load()
      showToast('success', 'Замок привязан к номеру')
    } catch (e) {
      showToast('error', e instanceof Error ? e.message : 'Ошибка привязки замка')
    } finally {
      setAddLoading(false)
    }
  }

  const handleStartEdit = (m: RoomLockMapping) => {
    setEditingMac(m.lockMac)
    setEditRoomId(m.roomId ?? '')
    setEditLockMac(m.lockMac)
    setEditLockName(m.lockName ?? '')
  }

  const handleSaveEdit = async () => {
    if (!editingMac) return
    setEditLoading(true)
    try {
      const newMac = editLockMac.replace(/:/g, '').toUpperCase()
      if (newMac !== editingMac) {
        // MAC changed — delete old, create new
        await api.ttlock.unmapRoom(slug, editingMac)
      }
      await api.ttlock.mapRoom(slug, {
        roomId:   editRoomId || undefined as any,
        lockMac:  newMac,
        lockName: editLockName.trim() || undefined,
      })
      setEditingMac(null)
      await load()
      showToast('success', 'Привязка обновлена')
    } catch (e) {
      showToast('error', e instanceof Error ? e.message : 'Ошибка сохранения')
    } finally {
      setEditLoading(false)
    }
  }

  const handleUnmap = async (lockMac: string) => {
    try {
      await api.ttlock.unmapRoom(slug, lockMac)
      await load()
      showToast('success', 'Привязка удалена')
    } catch {
      showToast('error', 'Ошибка удаления привязки')
    }
  }

  const handleLoadPorts = async (wsId: string) => {
    setWsPortsLoading(prev => ({ ...prev, [wsId]: true }))
    try {
      const result = await api.workstations.listPorts(slug, wsId)
      setWsPorts(prev => ({ ...prev, [wsId]: result.ports }))
    } catch {
      // agent offline — fallback to text input
    } finally {
      setWsPortsLoading(prev => ({ ...prev, [wsId]: false }))
    }
  }

  if (loading) {
    return (
      <div className="flex items-center justify-center h-64">
        <RefreshCw size={24} className="animate-spin text-brand-600" />
      </div>
    )
  }

  const unmappedRooms = rooms.filter(r => !mappings.some(m => m.roomId === r.id))

  return (
    <div className="max-w-3xl mx-auto p-6 space-y-6">

      {/* Floating toasts */}
      <ToastContainer toasts={toasts} onRemove={removeToast} />

      {/* Header */}
      <div className="flex items-center gap-3">
        <div className="w-10 h-10 rounded-xl bg-brand-100 dark:bg-brand-900/30 flex items-center justify-center">
          <KeyRound size={20} className="text-brand-600 dark:text-brand-400" />
        </div>
        <div>
          <h1 className="text-xl font-bold text-slate-900 dark:text-slate-100">Электронные замки TTLock</h1>
          <p className="text-sm text-slate-500">Автоматическая выдача карт-ключей через программу TTHotel</p>
        </div>
      </div>

      {/* ── Шаг 1: Подключение к TTHotel ── */}
      <Section title="Шаг 1 — Подключение к TTHotel">
        <div className="space-y-5">
          <Hint>
            Откройте программу <strong>TTHotel</strong> на Windows-компьютере →{' '}
            <strong>Настройки → Интеграции</strong>. Скопируйте поля Client ID и Client Secret.
          </Hint>

          {/* Включить/выключить */}
          <div className="flex items-center justify-between">
            <div>
              <p className="font-medium text-slate-800 dark:text-slate-200">Активировать модуль</p>
              <p className="text-xs text-slate-500 mt-0.5">Включает кнопку «Выдать ключ» в карточке бронирования</p>
            </div>
            <button
              onClick={() => setIsEnabled(e => !e)}
              className={cn(
                'relative w-11 h-6 rounded-full transition-colors',
                isEnabled ? 'bg-brand-500' : 'bg-slate-300 dark:bg-slate-600',
              )}
            >
              <span className={cn(
                'absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform',
                isEnabled && 'translate-x-5',
              )} />
            </button>
          </div>

          <div className="grid grid-cols-2 gap-4">
            <div>
              <label className="form-label">Client ID <span className="font-normal text-slate-400">(из TTHotel → Интеграция с PMS)</span></label>
              <input
                type="text"
                value={clientId}
                onChange={e => setClientId(e.target.value)}
                placeholder="0004e6c0cdb64324..."
                className="input w-full font-mono text-sm"
              />
            </div>
            <div>
              <label className="form-label">Client Secret <span className="font-normal text-slate-400">(нажмите «Просмотр» в TTHotel)</span></label>
              <PasswordInput
                value={clientSecret}
                onChange={setClientSecret}
                placeholder={config?.clientId ? '••••••• (не изменится)' : 'Нажмите «Просмотр» в TTHotel → Интеграция с PMS'}
              />
            </div>
          </div>

          {/* Секторы карты */}
          <div>
            <label className="form-label">Секторы карты</label>
            <SectorPicker value={cardSectors} onChange={setCardSectors} />
          </div>

          <div className="flex items-center gap-2">
            <button onClick={handleTestApi} disabled={testingApi || saving} className="btn-secondary flex items-center gap-2">
              {testingApi ? <RefreshCw size={15} className="animate-spin" /> : <Globe size={15} />}
              Проверить подключение
            </button>
            <button onClick={handleSave} disabled={saving} className="btn-primary flex items-center gap-2">
              {saving ? <RefreshCw size={15} className="animate-spin" /> : <Save size={15} />}
              Сохранить
            </button>
          </div>
        </div>
      </Section>

      {/* ── Шаг 2: Рабочие места и энкодеры ── */}
      <Section title="Шаг 2 — Рабочие места и энкодеры">
        <div className="space-y-4">
          <Hint>
            Для каждого рабочего места укажите COM-порт USB-энкодера карт (найти в Диспетчере устройств → Порты).
            Нажмите <strong>↺</strong> рядом с полем, чтобы загрузить список портов с агента.
            Агент должен быть онлайн.
          </Hint>

          {workstations.length === 0 && (
            <p className="text-sm text-slate-500">Нет рабочих мест. Добавьте их на странице «Оборудование».</p>
          )}
          <div className="space-y-3">
            {workstations.map(ws => {
              const hasPort     = !!wsComPorts[ws.id]?.trim()
              const portList    = wsPorts[ws.id]
              const portsLoading = wsPortsLoading[ws.id] ?? false

              return (
                <div
                  key={ws.id}
                  className={cn(
                    'flex items-center gap-3 p-3 rounded-xl border bg-slate-50 dark:bg-slate-800/40',
                    hasPort
                      ? 'border-slate-200 dark:border-slate-700'
                      : 'border-slate-200 dark:border-slate-700 opacity-60',
                  )}
                >
                  {/* Online dot */}
                  <div className={cn('w-2.5 h-2.5 rounded-full shrink-0', ws.isOnline ? 'bg-emerald-500' : 'bg-slate-300 dark:bg-slate-600')} />

                  {/* Name */}
                  <div className="flex-1 min-w-0">
                    <p className="text-sm font-medium text-slate-800 dark:text-slate-200 truncate">{ws.name}</p>
                    {ws.hostname && <p className="text-xs text-slate-400 truncate">{ws.hostname}</p>}
                    {!hasPort && (
                      <p className="text-xs text-amber-500 dark:text-amber-400 mt-0.5">Энкодер не настроен</p>
                    )}
                  </div>

                  {/* COM port */}
                  <div className="flex items-center gap-1 shrink-0">
                    {portList && portList.length > 0 ? (
                      <select
                        className="input w-36 text-sm font-mono py-1.5"
                        value={wsComPorts[ws.id] ?? ''}
                        onChange={e => setWsComPorts(prev => ({ ...prev, [ws.id]: e.target.value }))}
                      >
                        <option value="">— Выберите —</option>
                        {portList.map(p => (
                          <option key={p.port} value={p.port}>
                            {p.port}{p.description ? ` — ${p.description}` : ''}
                          </option>
                        ))}
                      </select>
                    ) : portList && portList.length === 0 ? (
                      <span className="text-xs text-slate-400 w-36 text-center">Порты не найдены</span>
                    ) : (
                      <input
                        type="text"
                        className="input w-28 text-sm font-mono"
                        placeholder="COM3"
                        value={wsComPorts[ws.id] ?? ''}
                        onChange={e => setWsComPorts(prev => ({ ...prev, [ws.id]: e.target.value }))}
                      />
                    )}

                    {/* Load ports button */}
                    <button
                      type="button"
                      onClick={() => handleLoadPorts(ws.id)}
                      disabled={portsLoading || !ws.isOnline}
                      title={!ws.isOnline ? 'Агент офлайн' : 'Загрузить список COM-портов'}
                      className="btn-secondary p-1.5 shrink-0"
                    >
                      {portsLoading
                        ? <RefreshCw size={13} className="animate-spin" />
                        : <RefreshCw size={13} />}
                    </button>
                  </div>

                  {/* Save COM port */}
                  <button
                    type="button"
                    disabled={wsSaving[ws.id]}
                    onClick={async () => {
                      setWsSaving(prev => ({ ...prev, [ws.id]: true }))
                      try {
                        await api.workstations.update(slug, ws.id, { ttlockComPort: wsComPorts[ws.id]?.trim() || null })
                        showToast('success', `COM-порт для «${ws.name}» сохранён`)
                        await load()
                      } catch { showToast('error', 'Ошибка сохранения') }
                      finally { setWsSaving(prev => ({ ...prev, [ws.id]: false })) }
                    }}
                    className="btn-secondary p-1.5 shrink-0"
                    title="Сохранить COM-порт"
                  >
                    {wsSaving[ws.id]
                      ? <RefreshCw size={13} className="animate-spin" />
                      : <Save size={13} />}
                  </button>

                  {/* Test encoder */}
                  <button
                    type="button"
                    disabled={!ws.isOnline || !hasPort || wsTesting[ws.id]}
                    onClick={async () => {
                      setWsTesting(prev => ({ ...prev, [ws.id]: true }))
                      try {
                        await api.ttlock.testEncoder(slug, ws.id)
                        showToast('success', `Энкодер на «${ws.name}» отвечает — всё готово!`)
                      } catch (e) {
                        const msg = e instanceof Error ? e.message : 'Ошибка проверки'
                        showToast('error', isDllEntryError(msg)
                          ? `Имена функций в DLL не совпадают. Проверьте index.js в TTHotel.`
                          : msg)
                      } finally { setWsTesting(prev => ({ ...prev, [ws.id]: false })) }
                    }}
                    className="btn-secondary p-1.5 shrink-0"
                    title={!ws.isOnline ? 'Агент офлайн' : !hasPort ? 'Укажите COM-порт' : 'Проверить энкодер'}
                  >
                    {wsTesting[ws.id]
                      ? <RefreshCw size={13} className="animate-spin" />
                      : <Zap size={13} />}
                  </button>
                </div>
              )
            })}
          </div>
        </div>
      </Section>

      {/* ── Шаг 3: Привязка номеров к замкам ── */}
      <Section title="Шаг 3 — Привязка номеров к замкам">
        <div className="space-y-5">
          <Hint>
            MAC-адрес замка: войдите на <strong>lock.ttlock.com</strong> →{' '}
            нажмите <strong>Manage</strong> у нужного замка → иконка шестерёнки →{' '}
            <strong>Basic</strong> → скопируйте поле <strong>MAC</strong>.
            Введите без двоеточий: <code className="bg-blue-100 dark:bg-blue-900/40 px-1 rounded font-mono">42A6BBF5ECE5</code>
          </Hint>

          {/* Импорт из TTHotel XLS */}
          <div className="flex items-center justify-between">
            <p className="text-sm text-slate-500">Или импортируйте привязки из файла TTHotel</p>
            <button
              type="button"
              onClick={() => importFileRef.current?.click()}
              className="btn-secondary flex items-center gap-1.5 text-sm"
            >
              <Upload size={14} />
              Импорт из TTHotel (.xls)
            </button>
            <input
              ref={importFileRef}
              type="file"
              accept=".xls,.xlsx"
              className="hidden"
              onChange={e => {
                const f = e.target.files?.[0]
                if (f) { handleImportFile(f); e.target.value = '' }
              }}
            />
          </div>

          {/* Превью импорта */}
          {importRows && (
            <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
              <div className="px-4 py-3 bg-slate-50 dark:bg-slate-800/50 flex items-center justify-between">
                <span className="text-sm font-medium text-slate-700 dark:text-slate-300">
                  Найдено замков: {importRows.length}
                  {' · '}
                  <span className="text-emerald-600 dark:text-emerald-400">
                    совпадений: {importRows.filter(r => r.roomId && !r.skip).length}
                  </span>
                  {importRows.some(r => !r.roomId) && (
                    <span className="text-amber-500 ml-1">
                      · без номера: {importRows.filter(r => !r.roomId).length}
                    </span>
                  )}
                  {importRows.some(r => r.skip) && (
                    <span className="text-slate-400 ml-1">
                      · уже привязаны: {importRows.filter(r => r.skip).length}
                    </span>
                  )}
                </span>
                <button onClick={() => setImportRows(null)} className="text-slate-400 hover:text-slate-600">
                  <X size={14} />
                </button>
              </div>
              <div className="divide-y divide-slate-100 dark:divide-slate-800 max-h-72 overflow-y-auto">
                {importRows.map((row, i) => {
                  const matchedRoom = rooms.find(r => r.id === row.roomId)
                  return (
                    <div
                      key={i}
                      className={cn(
                        'flex items-center gap-3 px-4 py-2.5 text-sm',
                        row.skip ? 'opacity-40' : '',
                      )}
                    >
                      <div className="w-4 shrink-0">
                        {row.skip
                          ? <Check size={13} className="text-slate-400" />
                          : row.roomId
                            ? <Check size={13} className="text-emerald-500" />
                            : <AlertCircle size={13} className="text-amber-400" />}
                      </div>
                      <span className="w-10 font-mono text-slate-500 shrink-0">{row.doorName}</span>
                      <span className="flex-1 font-mono text-xs text-slate-600 dark:text-slate-400">{row.lockMac}</span>
                      <select
                        className="input text-xs py-1 w-44"
                        value={row.roomId}
                        disabled={row.skip}
                        onChange={e => setImportRows(prev => prev!.map((r, j) =>
                          j === i ? { ...r, roomId: e.target.value } : r
                        ))}
                      >
                        <option value="">— номер не выбран —</option>
                        {rooms.map(r => (
                          <option key={r.id} value={r.id}>
                            №{r.number}{r.name ? ` ${r.name}` : ''}
                          </option>
                        ))}
                      </select>
                      {row.skip && <span className="text-xs text-slate-400 shrink-0">уже привязан</span>}
                      {!row.skip && matchedRoom && <span className="text-xs text-emerald-600 shrink-0">авто</span>}
                    </div>
                  )
                })}
              </div>
              <div className="px-4 py-3 bg-slate-50 dark:bg-slate-800/50 flex justify-end gap-2">
                <button onClick={() => setImportRows(null)} className="btn-secondary text-sm">
                  Отмена
                </button>
                <button
                  onClick={handleImport}
                  disabled={importing || importRows.every(r => r.skip)}
                  className="btn-primary flex items-center gap-2 text-sm"
                >
                  {importing ? <RefreshCw size={14} className="animate-spin" /> : <Save size={14} />}
                  Импортировать {importRows.filter(r => !r.skip).length} замков
                </button>
              </div>
            </div>
          )}

          {/* Форма добавления */}
          {unmappedRooms.length > 0 && (
            <div className="p-4 bg-slate-50 dark:bg-slate-800/50 rounded-xl space-y-3">
              <p className="text-sm font-medium text-slate-700 dark:text-slate-300">Добавить привязку</p>
              <div className="grid grid-cols-3 gap-3">
                <div>
                  <label className="form-label">Номер</label>
                  <select value={addRoomId} onChange={e => setAddRoomId(e.target.value)} className="input w-full">
                    <option value="">— Выберите —</option>
                    {unmappedRooms.map(r => (
                      <option key={r.id} value={r.id}>
                        №{r.number}{r.name ? ` ${r.name}` : ''}
                      </option>
                    ))}
                  </select>
                </div>
                <div>
                  <label className="form-label">MAC замка</label>
                  <input
                    type="text"
                    value={addLockMac}
                    onChange={e => setAddLockMac(e.target.value.replace(/[^0-9A-Fa-f:]/g, '').toUpperCase())}
                    placeholder="42A6BBF5ECE5"
                    maxLength={17}
                    className="input w-full font-mono text-sm"
                  />
                </div>
                <div>
                  <label className="form-label">Название (необяз.)</label>
                  <input
                    type="text"
                    value={addLockName}
                    onChange={e => setAddLockName(e.target.value)}
                    placeholder="Замок номера 101"
                    className="input w-full"
                  />
                </div>
              </div>
              <button
                onClick={handleAddMapping}
                disabled={!addRoomId || !addLockMac.trim() || addLoading}
                className="btn-primary flex items-center gap-2"
              >
                {addLoading ? <RefreshCw size={15} className="animate-spin" /> : <Plus size={15} />}
                Привязать
              </button>
            </div>
          )}

          {/* Список привязок */}
          {mappings.length > 0 ? (
            <div className="space-y-2">
              {mappings.map((m, i) => (
                <div key={m.lockMac ?? i}>
                  {editingMac === m.lockMac ? (
                    /* ── Режим редактирования ── */
                    <div className="p-4 rounded-xl border-2 border-brand-400 bg-brand-50 dark:bg-brand-900/10 space-y-3">
                      <div className="grid grid-cols-3 gap-3">
                        <div>
                          <label className="form-label">Номер</label>
                          <select
                            value={editRoomId}
                            onChange={e => setEditRoomId(e.target.value)}
                            className="input w-full"
                          >
                            <option value="">— Без комнаты —</option>
                            {rooms.map(r => (
                              <option key={r.id} value={r.id}>
                                №{r.number}{r.name ? ` ${r.name}` : ''}
                              </option>
                            ))}
                          </select>
                        </div>
                        <div>
                          <label className="form-label">MAC замка</label>
                          <input
                            type="text"
                            value={editLockMac}
                            onChange={e => setEditLockMac(e.target.value.replace(/[^0-9A-Fa-f:]/g, '').toUpperCase())}
                            maxLength={17}
                            className="input w-full font-mono text-sm"
                          />
                        </div>
                        <div>
                          <label className="form-label">Название (необяз.)</label>
                          <input
                            type="text"
                            value={editLockName}
                            onChange={e => setEditLockName(e.target.value)}
                            placeholder="Замок номера 101"
                            className="input w-full"
                          />
                        </div>
                      </div>
                      <div className="flex gap-2">
                        <button
                          onClick={handleSaveEdit}
                          disabled={editLoading || !editLockMac.trim()}
                          className="btn-primary flex items-center gap-2 text-sm"
                        >
                          {editLoading ? <RefreshCw size={13} className="animate-spin" /> : <Check size={13} />}
                          Сохранить
                        </button>
                        <button
                          onClick={() => setEditingMac(null)}
                          disabled={editLoading}
                          className="btn-secondary text-sm"
                        >
                          Отмена
                        </button>
                      </div>
                    </div>
                  ) : (
                    /* ── Просмотр ── */
                    <div className={cn(
                      'flex items-center justify-between p-3 rounded-lg border bg-white dark:bg-slate-900',
                      m.roomId
                        ? 'border-slate-200 dark:border-slate-700'
                        : 'border-amber-200 dark:border-amber-700/40',
                    )}>
                      <div className="flex items-center gap-3">
                        <div className={cn(
                          'w-8 h-8 rounded-lg flex items-center justify-center shrink-0',
                          m.roomId
                            ? 'bg-brand-50 dark:bg-brand-900/20'
                            : 'bg-amber-50 dark:bg-amber-900/20',
                        )}>
                          <Lock size={14} className={m.roomId ? 'text-brand-600 dark:text-brand-400' : 'text-amber-500'} />
                        </div>
                        <div>
                          <p className="text-sm font-medium text-slate-800 dark:text-slate-200">
                            {m.roomId
                              ? <>Номер {m.roomNumber}{m.roomName ? ` — ${m.roomName}` : ''}</>
                              : <span className="text-amber-600 dark:text-amber-400">Комната не привязана{m.lockName ? ` — ${m.lockName}` : ''}</span>
                            }
                          </p>
                          <p className="text-xs text-slate-500 font-mono">
                            {m.lockMac}
                            {m.roomId && m.lockName && <span className="text-slate-400 font-sans ml-2">{m.lockName}</span>}
                          </p>
                        </div>
                      </div>
                      <div className="flex items-center gap-1">
                        <button
                          onClick={() => handleStartEdit(m)}
                          className="btn-ghost p-2 text-slate-400 hover:text-brand-600"
                          title="Редактировать"
                        >
                          <Pencil size={14} />
                        </button>
                        <button
                          onClick={() => handleUnmap(m.lockMac)}
                          className="btn-ghost p-2 text-red-400 hover:text-red-600"
                          title="Удалить привязку"
                        >
                          <Trash2 size={14} />
                        </button>
                      </div>
                    </div>
                  )}
                </div>
              ))}
            </div>
          ) : (
            <div className="text-center py-8 text-slate-400 dark:text-slate-600">
              <Unlock size={32} className="mx-auto mb-2 opacity-40" />
              <p className="text-sm">Нет привязанных замков</p>
              <p className="text-xs mt-1">Добавьте MAC-адреса замков для каждого номера</p>
            </div>
          )}

          {rooms.length > 0 && unmappedRooms.length === 0 && mappings.length > 0 && (
            <p className="text-xs text-emerald-600 dark:text-emerald-400 flex items-center gap-1">
              <Check size={12} /> Все номера привязаны к замкам
            </p>
          )}
        </div>
      </Section>

    </div>
  )
}

export default TTLockPage
