import { useEffect, useLayoutEffect, useMemo, useRef, useState, type KeyboardEvent } from 'react'
import { createPortal } from 'react-dom'
import { Plus } from 'lucide-react'
import { api } from '@/lib/api'
import { BarcodeType, type Product } from '@/lib/types'
import { ProductQuickCreateModal } from '@/components/ProductQuickCreateModal'

export interface QuickSearchItem {
  id: string
  name: string
  article: string | null
  defaultBarcode: string | null
  referencePrice: number | null
  stockQty: number | null
}

export interface AddedProduct {
  id: string
  name: string
  article: string | null
  /** Основной штрихкод (IsPrimary), либо первый по порядку. */
  barcode: string | null
  referencePrice: number | null
  unitName: string | null
  cost: number | null
  prices?: { amount: number }[]
}

interface Props {
  storeId: string
  disabled?: boolean
  /** Колбэк добавления / инкремента строки. Возвращает true если был выполнен
   * инкремент (для всплывающего «Кол-во +1»), false если добавлена новая. */
  onPick: (product: AddedProduct) => boolean
  /** Текущее кол-во строк — для UX (autoscroll, sticky-position при необходимости). */
  linesCount?: number
}

const isLikelyBarcode = (s: string) => /^\d{8,14}$/.test(s)
function detectBarcodeType(s: string): BarcodeType {
  if (s.length === 8) return BarcodeType.Ean8
  if (s.length === 12) return BarcodeType.Upca
  if (s.length === 14) return BarcodeType.Other
  return BarcodeType.Ean13
}

/** Inline-добавление строк в документ: единый input, автокомплит по
 * имени/артикулу/штрихкоду со стоковыми бэйджами, мгновенный pick по
 * сканеру (Enter после цифр), создание товара «на лету» если ничего
 * не нашлось. После каждого добавления input очищается и возвращает
 * фокус — чтобы можно было сканировать партию подряд. */
export function SupplyLineQuickAdd({ storeId, disabled, onPick }: Props) {
  const [query, setQuery] = useState('')
  const [debounced, setDebounced] = useState('')
  const [items, setItems] = useState<QuickSearchItem[]>([])
  const [open, setOpen] = useState(false)
  const [highlight, setHighlight] = useState(0)
  const [loading, setLoading] = useState(false)
  // По умолчанию показываем первые 10 матчей. Ссылка «Ещё N товаров»
  // расширяет dropdown до полного списка (limit=50 на запрос).
  const VISIBLE_LIMIT = 10
  const [showAll, setShowAll] = useState(false)
  const [hint, setHint] = useState<string | null>(null)
  const [createPrefill, setCreatePrefill] = useState<null | (
    | { kind: 'barcode'; value: string; barcodeType: BarcodeType }
    | { kind: 'article'; value: string }
    | { kind: 'name'; value: string }
  )>(null)
  const inputRef = useRef<HTMLInputElement>(null)
  const wrapRef = useRef<HTMLDivElement>(null)
  const dropdownRef = useRef<HTMLDivElement>(null)
  const [dropdownPos, setDropdownPos] = useState<{ bottom: number; left: number; width: number } | null>(null)

  // Считаем позицию dropdown'а по rect input'а. Dropdown открывается ВВЕРХ
  // от input'а (МС-стиль): фиксируем `bottom` относительно низа viewport,
  // вычисленный из input.top, и max-height 60vh — расти вверх в пределах
  // экрана. Без этого portal в body рендерил бы попап в углу.
  const recomputePos = () => {
    const el = inputRef.current
    if (!el) return
    const r = el.getBoundingClientRect()
    setDropdownPos({ bottom: window.innerHeight - r.top + 4, left: r.left, width: r.width })
  }
  useLayoutEffect(() => {
    if (!open) return
    recomputePos()
    const onScrollOrResize = () => recomputePos()
    window.addEventListener('scroll', onScrollOrResize, true)
    window.addEventListener('resize', onScrollOrResize)
    return () => {
      window.removeEventListener('scroll', onScrollOrResize, true)
      window.removeEventListener('resize', onScrollOrResize)
    }
  }, [open])

  // Автофокус при монтировании
  useEffect(() => {
    if (!disabled) inputRef.current?.focus()
  }, [disabled])

  // Debounce 200ms
  useEffect(() => {
    const t = setTimeout(() => setDebounced(query), 200)
    return () => clearTimeout(t)
  }, [query])

  // Поисковый запрос на каждое изменение debounced query. AbortController
  // обрывает устаревший запрос, чтобы при быстрых последовательных вводах
  // (или сразу после скана) старый ответ не подменил свежий список.
  useEffect(() => {
    const q = debounced.trim()
    if (q.length === 0) { setItems([]); setOpen(false); return }
    const ac = new AbortController()
    setLoading(true)
    setShowAll(false)
    api.get<QuickSearchItem[]>('/api/catalog/products/quick-search', {
      params: { search: q, storeId: storeId || undefined, limit: 50 },
      signal: ac.signal,
    }).then((res) => {
      setItems(res.data)
      setOpen(true)
      setHighlight(0)
    }).catch((e) => {
      if ((e as { name?: string }).name === 'CanceledError' || (e as { code?: string }).code === 'ERR_CANCELED') return
      setItems([])
    }).finally(() => setLoading(false))
    return () => ac.abort()
  }, [debounced, storeId])

  // Outside click — закрыть dropdown. Dropdown рендерится в Portal, поэтому
  // проверяем и wrap (input), и сам dropdown.
  useEffect(() => {
    if (!open) return
    const onDoc = (e: MouseEvent) => {
      const t = e.target as Node
      const inWrap = wrapRef.current?.contains(t)
      const inDropdown = dropdownRef.current?.contains(t)
      if (!inWrap && !inDropdown) setOpen(false)
    }
    document.addEventListener('mousedown', onDoc)
    return () => document.removeEventListener('mousedown', onDoc)
  }, [open])

  // Авто-исчезновение всплывающей подсказки
  useEffect(() => {
    if (!hint) return
    const t = setTimeout(() => setHint(null), 1500)
    return () => clearTimeout(t)
  }, [hint])

  // Возврат фокуса после добавления строки — двойной guarded focus:
  // сразу + через requestAnimationFrame, чтобы перебить любые ререндеры,
  // которые могут увести фокус в null. Без этого подряд сканирование
  // ломается на 2-3 скане, когда DOM подменяет input.
  const refocus = () => {
    inputRef.current?.focus()
    requestAnimationFrame(() => inputRef.current?.focus())
  }

  const addById = async (id: string) => {
    // Очищаем поле и возвращаем фокус СРАЗУ — пока сетевой запрос в полёте,
    // юзер уже может начать ввод следующего штрихкода.
    setQuery(''); setDebounced(''); setItems([]); setOpen(false)
    refocus()
    try {
      const full = (await api.get<Product>(`/api/catalog/products/${id}`)).data
      const primaryBarcode = (full.barcodes ?? []).slice().sort((a, b) => Number(b.isPrimary) - Number(a.isPrimary))[0]?.code ?? null
      const incremented = onPick({
        id: full.id,
        name: full.name,
        article: full.article,
        barcode: primaryBarcode,
        referencePrice: full.referencePrice,
        unitName: full.unitName,
        cost: full.cost,
        prices: full.prices?.map((p) => ({ amount: p.amount })),
      })
      if (incremented) setHint('Кол-во увеличено на 1')
    } finally {
      refocus()
    }
  }

  // Точный barcode-поиск (для сканера: Enter сразу после цифр)
  const tryScanByBarcode = async (q: string): Promise<boolean> => {
    if (!isLikelyBarcode(q)) return false
    try {
      const res = await api.get(`/api/catalog/products/by-barcode/${encodeURIComponent(q)}`, {
        params: { storeId: storeId || undefined },
      })
      const data = res.data as QuickSearchItem | { items: QuickSearchItem[] }
      if ('items' in data && Array.isArray(data.items)) {
        // Несколько матчей — отдадим в dropdown через items, пользователь выберет.
        setItems(data.items); setOpen(true); setHighlight(0)
        return true
      }
      const it = data as QuickSearchItem
      await addById(it.id)
      return true
    } catch (e) {
      const status = (e as { response?: { status?: number } }).response?.status
      if (status === 404) {
        // Не нашли — предложим создать с pre-fill штрихкода
        setCreatePrefill({ kind: 'barcode', value: q, barcodeType: detectBarcodeType(q) })
        return true
      }
      return false
    }
  }

  const onKeyDown = async (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Escape') { setOpen(false); return }
    if (e.key === 'Tab') { setOpen(false); return }
    const visibleLen = (showAll ? items.length : Math.min(items.length, VISIBLE_LIMIT))
    if (e.key === 'ArrowDown') {
      e.preventDefault()
      setHighlight((h) => Math.min(h + 1, Math.max(0, visibleLen - 1)))
      return
    }
    if (e.key === 'ArrowUp') {
      e.preventDefault()
      setHighlight((h) => Math.max(0, h - 1))
      return
    }
    if (e.key === 'Enter') {
      e.preventDefault()
      const q = query.trim()
      if (q.length === 0) return
      // Сначала пробуем сканер-флоу — точный barcode lookup
      if (await tryScanByBarcode(q)) return
      // Иначе берём подсвеченный пункт из видимой части dropdown'а
      const visible = showAll ? items : items.slice(0, VISIBLE_LIMIT)
      if (visible.length > 0) {
        const target = visible[highlight] ?? visible[0]
        await addById(target.id)
        return
      }
      // Ничего не нашли — открываем модалку создания с pre-fill
      const kind = isLikelyBarcode(q) ? 'barcode' : /^\d+$/.test(q) ? 'article' : 'name'
      if (kind === 'barcode') setCreatePrefill({ kind, value: q, barcodeType: detectBarcodeType(q) })
      else if (kind === 'article') setCreatePrefill({ kind, value: q })
      else setCreatePrefill({ kind: 'name', value: q })
    }
  }

  const onCreated = async (p: Product) => {
    const primaryBarcode = (p.barcodes ?? []).slice().sort((a, b) => Number(b.isPrimary) - Number(a.isPrimary))[0]?.code ?? null
    onPick({
      id: p.id,
      name: p.name,
      article: p.article,
      barcode: primaryBarcode,
      referencePrice: p.referencePrice,
      unitName: p.unitName,
      cost: p.cost,
      prices: p.prices?.map((pr) => ({ amount: pr.amount })),
    })
    setQuery(''); setDebounced(''); setItems([]); setOpen(false)
    refocus()
  }

  const showCreateRow = useMemo(() => {
    const q = query.trim()
    return q.length > 0 && !loading
  }, [query, loading])

  return (
    <div ref={wrapRef} className="relative">
      <input
        ref={inputRef}
        disabled={disabled}
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        onKeyDown={onKeyDown}
        onFocus={() => { if (items.length > 0 || query.trim()) setOpen(true) }}
        placeholder="Сканируйте штрихкод или начните вводить название / артикул / штрихкод…"
        className="w-full h-11 rounded-md border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 px-4 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-brand)] disabled:opacity-60 disabled:bg-slate-50"
      />
      {hint && (
        <div className="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-emerald-600 bg-emerald-50 px-2 py-0.5 rounded">
          {hint}
        </div>
      )}
      {open && (query.trim().length > 0 || items.length > 0) && dropdownPos && createPortal(
        <div
          ref={dropdownRef}
          style={{ position: 'fixed', bottom: dropdownPos.bottom, left: dropdownPos.left, width: dropdownPos.width, maxHeight: '60vh' }}
          className="z-[100] rounded-md border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-lg flex flex-col overflow-hidden"
        >
          {loading && items.length === 0 ? (
            <div className="px-3 py-2 text-sm text-slate-400">Ищу…</div>
          ) : items.length === 0 ? (
            <div className="px-3 py-2 text-sm text-slate-400">Ничего не найдено</div>
          ) : (
            <ul className="py-1 overflow-y-auto">
              {(showAll ? items : items.slice(0, VISIBLE_LIMIT)).map((it, i) => (
                <li key={it.id}>
                  <button
                    type="button"
                    onMouseEnter={() => setHighlight(i)}
                    onClick={() => addById(it.id)}
                    className={`w-full flex items-center justify-between gap-3 px-3 py-1.5 text-left text-sm ${i === highlight ? 'bg-slate-100 dark:bg-slate-800' : ''}`}
                  >
                    <span className="flex-1 min-w-0 truncate">
                      {it.article && <span className="font-mono text-slate-400 mr-2">{it.article}</span>}
                      <span className="text-slate-900 dark:text-slate-100">{highlightMatch(it.name, query.trim())}</span>
                      {it.defaultBarcode && (
                        <span className="ml-2 text-xs text-slate-400 font-mono">{it.defaultBarcode}</span>
                      )}
                    </span>
                    <StockBadge qty={it.stockQty} />
                  </button>
                </li>
              ))}
            </ul>
          )}
          {!showAll && items.length > VISIBLE_LIMIT && (
            <div className="border-t border-slate-100 dark:border-slate-800">
              <button
                type="button"
                onClick={() => setShowAll(true)}
                className="w-full text-left px-3 py-2 text-sm text-[var(--color-brand)] hover:bg-slate-100 dark:hover:bg-slate-800"
              >
                Ещё {items.length - VISIBLE_LIMIT} {plural(items.length - VISIBLE_LIMIT, 'товар', 'товара', 'товаров')}
              </button>
            </div>
          )}
          {showCreateRow && (
            <div className="border-t border-slate-100 dark:border-slate-800">
              <button
                type="button"
                onClick={() => {
                  const q = query.trim()
                  const kind = isLikelyBarcode(q) ? 'barcode' : /^\d+$/.test(q) ? 'article' : 'name'
                  if (kind === 'barcode') setCreatePrefill({ kind, value: q, barcodeType: detectBarcodeType(q) })
                  else if (kind === 'article') setCreatePrefill({ kind, value: q })
                  else setCreatePrefill({ kind: 'name', value: q })
                  setOpen(false)
                }}
                className="w-full flex items-center gap-2 px-3 py-2 text-sm text-[var(--color-brand)] hover:bg-slate-100 dark:hover:bg-slate-800"
              >
                <Plus className="w-4 h-4" />
                {isLikelyBarcode(query.trim())
                  ? <>Создать товар со штрихкодом «{query.trim()}»</>
                  : <>Создать новый товар: «{query.trim()}»</>}
              </button>
            </div>
          )}
        </div>,
        document.body,
      )}

      <ProductQuickCreateModal
        open={createPrefill !== null}
        onClose={() => { setCreatePrefill(null); refocus() }}
        prefill={createPrefill}
        onCreated={onCreated}
      />
    </div>
  )
}

function StockBadge({ qty }: { qty: number | null }) {
  if (qty === null || qty === undefined) return null
  const cls = qty > 0
    ? 'bg-emerald-50 text-emerald-700 border-emerald-200'
    : qty < 0 ? 'bg-red-50 text-red-700 border-red-200'
    : 'bg-slate-50 text-slate-500 border-slate-200'
  return <span className={`text-xs font-mono px-1.5 py-0.5 rounded border ${cls}`}>{qty}</span>
}

function plural(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 < 12 || mod100 > 14)) return few
  return many
}

function highlightMatch(text: string, q: string) {
  if (!q) return text
  const i = text.toLowerCase().indexOf(q.toLowerCase())
  if (i < 0) return text
  return (
    <>
      {text.slice(0, i)}
      <mark className="bg-yellow-200 dark:bg-yellow-800 text-inherit px-0.5 rounded-sm">{text.slice(i, i + q.length)}</mark>
      {text.slice(i + q.length)}
    </>
  )
}
