import { useMemo, useRef, useLayoutEffect, type InputHTMLAttributes, type FormEvent } from 'react'
import { cn } from '@/lib/utils'

const inputClass = 'w-full h-10 rounded-md border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 px-3 text-sm leading-none focus:outline-none focus:ring-2 focus:ring-[var(--color-brand)] disabled:opacity-60 disabled:bg-slate-50 dark:disabled:bg-slate-800/60 tabular-nums'

const PREFIX = '+7 7'      // Залочено: «+», «7», пробел, «7» (мобильный код KZ).
const PREFIX_LEN = PREFIX.length // 4

/** Извлекает 9 пользовательских цифр из любой формы значения (canonical
 * «+77XXXXXXXXX», display «+7 7XX XXX XX XX», raw paste «8 707…», и т.д.).
 * Снимает по одной ведущей «8», «7», «7» — это код страны и мобильный
 * префикс KZ, оба зашиты в наш PREFIX. */
function rawToUserDigits(raw: string): string {
  let d = raw.replace(/\D/g, '')
  if (d[0] === '8') d = d.slice(1)
  if (d[0] === '7') d = d.slice(1)
  if (d[0] === '7') d = d.slice(1)
  return d.slice(0, 9)
}

/** Display из 9 пользовательских цифр. Формат: «+7 7XX XXX XX XX» — после
 * залоченного «+7 7» идут 2+3+2+2 = 9 цифр через пробелы. */
function userDigitsToDisplay(d: string): string {
  if (d.length === 0) return PREFIX
  if (d.length <= 2) return `${PREFIX}${d}`
  if (d.length <= 5) return `${PREFIX}${d.slice(0, 2)} ${d.slice(2)}`
  if (d.length <= 7) return `${PREFIX}${d.slice(0, 2)} ${d.slice(2, 5)} ${d.slice(5)}`
  return `${PREFIX}${d.slice(0, 2)} ${d.slice(2, 5)} ${d.slice(5, 7)} ${d.slice(7, 9)}`
}

/** Сколько цифровых символов стоит ДО позиции cursor в строке value. */
function digitsBeforeCursor(value: string, cursor: number): number {
  let count = 0
  for (let i = 0; i < cursor && i < value.length; i++) {
    if (/\d/.test(value[i])) count++
  }
  return count
}

/** Позиция в value сразу ПОСЛЕ n-й цифры (1-indexed). Если n больше чем
 * есть цифр — возвращает конец строки. */
function cursorAfterNthDigit(value: string, n: number): number {
  if (n <= 0) return 0
  let count = 0
  for (let i = 0; i < value.length; i++) {
    if (/\d/.test(value[i])) {
      count++
      if (count === n) return i + 1
    }
  }
  return value.length
}

interface PhoneInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type'> {
  /** Каноничное значение «+77XXXXXXXXX» либо пустая строка. */
  value: string
  onChange: (value: string) => void
}

/** Поле ввода телефона Казахстана. Префикс «+7 7» (4 символа) залочен —
 * курсор не пускается в позиции 0..3, выделение клампится на digit-зону.
 * В остальном работает как обычный input: native selection, backspace,
 * click, Cmd+A, paste, drag — всё нативное. Единственное ограничение —
 * не-цифры блокируются через onBeforeInput до того как попасть в value. */
export function PhoneInput({ value, onChange, className, disabled, ...rest }: PhoneInputProps) {
  const ref = useRef<HTMLInputElement>(null)
  const userDigits = useMemo(() => rawToUserDigits(value), [value])
  const display = userDigitsToDisplay(userDigits)
  // Запоминаем сколько цифр стояло до курсора в момент редактирования —
  // после React-нормализации (которая может сдвинуть пробелы формата)
  // ставим курсор обратно после той же по счёту цифры. Без этого React
  // сбрасывал бы курсор в конец на каждом изменении value.
  const pendingDigitCountRef = useRef<number | null>(null)

  useLayoutEffect(() => {
    const n = pendingDigitCountRef.current
    pendingDigitCountRef.current = null
    if (n === null) return
    const el = ref.current
    if (!el) return
    const pos = Math.max(PREFIX_LEN, cursorAfterNthDigit(display, n))
    el.setSelectionRange(pos, pos)
  }, [display])

  // Блокируем вставку не-цифр (типинг + paste + IME). data === null для
  // deleteContentBackward и т.п. — там не вмешиваемся, пусть браузер удаляет.
  const handleBeforeInput = (e: FormEvent<HTMLInputElement>) => {
    const native = e.nativeEvent as InputEvent
    if (native.data && /\D/.test(native.data)) {
      e.preventDefault()
    }
  }

  // После любого нативного редактирования (печать/удаление/paste/drag-drop)
  // нормализуем значение. Запоминаем позицию курсора в digit-индексах,
  // чтобы useLayoutEffect восстановил её после ре-рендера.
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const cursorPos = e.target.selectionStart ?? e.target.value.length
    pendingDigitCountRef.current = digitsBeforeCursor(e.target.value, cursorPos)
    const newDigits = rawToUserDigits(e.target.value)
    onChange(newDigits.length > 0 ? `+77${newDigits}` : '')
  }

  // Не пускаем курсор/selection в префикс. onSelect ловит любые попытки
  // (клик, стрелки, Cmd+A) и мягко двигает на digit-зону.
  const guardCursor = (el: HTMLInputElement) => {
    const start = el.selectionStart ?? 0
    const end = el.selectionEnd ?? 0
    if (start < PREFIX_LEN || end < PREFIX_LEN) {
      el.setSelectionRange(Math.max(PREFIX_LEN, start), Math.max(PREFIX_LEN, end))
    }
  }

  const handleSelect = (e: React.SyntheticEvent<HTMLInputElement>) => guardCursor(e.currentTarget)
  const handleFocus = () => {
    const el = ref.current
    if (!el) return
    requestAnimationFrame(() => guardCursor(el))
  }

  return (
    <input
      ref={ref}
      type="tel"
      inputMode="tel"
      autoComplete="tel"
      value={display}
      onBeforeInput={handleBeforeInput}
      onChange={handleChange}
      onSelect={handleSelect}
      onFocus={handleFocus}
      disabled={disabled}
      placeholder={PREFIX}
      className={cn(inputClass, className)}
      {...rest}
    />
  )
}
