<template>
  <el-input
    :value="displayText"
    :placeholder="placeholder"
    :disabled="disabled"
    v-bind="$attrs"
    @input="onInput"
    @focus="onFocus"
    @blur="onBlur"
  />
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import type { FormattedNumberRounding } from '../utils/formattedNumber'
import {
  applyNumericInputDigitLimits,
  formatWithThousands,
  normalizeNumericValue,
  numberToEditString,
  sanitizeNumericInput,
  stripNumberGrouping,
  toFixedDecimalString,
} from '../utils/formattedNumber'

const props = withDefaults(
  defineProps<{
    value?: unknown
    placeholder?: string
    disabled?: boolean
    /** 整数部分允许的最大位数，默认 5 */
    integerDigits?: number
    /** 小数保留位数，默认 6 */
    decimalPlaces?: number
    /** 进位方式，默认四舍五入 */
    rounding?: FormattedNumberRounding
    /**
     * 为 true 时在输入过程中按 integerDigits / decimalPlaces 限制可输入位数；
     * 为 false 时仅失焦后规范化，输入阶段不截断长度。
     */
    inputLimit?: boolean
  }>(),
  {
    integerDigits: 6,
    decimalPlaces: 6,
    rounding: 'round',
    inputLimit: true,
  }
)

const emit = defineEmits<{ (e: 'input', value: unknown): void }>()

const focused = ref(false)
const displayText = ref('')

const intN = () => Math.max(0, Math.floor(props.integerDigits ?? 6))
const decM = () => Math.max(0, Math.floor(props.decimalPlaces ?? 6))

function parseExternalToNumber(v: unknown): number | null {
  if (v === null || v === undefined || v === '') return null
  if (typeof v === 'number') {
    return Number.isFinite(v) ? v : null
  }
  const s = stripNumberGrouping(String(v))
  if (s === '' || s === '-') return null
  const num = Number(s)
  return Number.isFinite(num) ? num : null
}

function syncDisplayFromValue() {
  if (focused.value) return
  const num = parseExternalToNumber(props.value)
  if (num === null) {
    displayText.value = ''
    return
  }
  displayText.value = formatWithThousands(
    normalizeNumericValue(num, intN(), decM(), props.rounding),
    decM()
  )
}

watch(
  () => [props.value, props.integerDigits, props.decimalPlaces, props.rounding],
  () => syncDisplayFromValue(),
  { immediate: true, deep: true }
)

function emitStoredValue(normalized: number) {
  emit('input', toFixedDecimalString(normalized, decM()))
}

function onInput(val: string) {
  let clean = sanitizeNumericInput(val)
  if (props.inputLimit !== false) {
    clean = applyNumericInputDigitLimits(clean, intN(), decM())
  }
  displayText.value = clean
  if (clean === '' || clean === '-') return
  const num = Number(clean)
  if (!Number.isFinite(num)) return
  const normalized = normalizeNumericValue(num, intN(), decM(), props.rounding)
  emitStoredValue(normalized)
}

function onFocus() {
  focused.value = true
  const raw = stripNumberGrouping(displayText.value)
  const num = parseExternalToNumber(raw === '' ? props.value : raw)
  if (num === null) {
    displayText.value = ''
    return
  }
  displayText.value = numberToEditString(
    normalizeNumericValue(num, intN(), decM(), props.rounding),
    decM()
  )
}

function onBlur() {
  focused.value = false
  const raw = stripNumberGrouping(displayText.value)
  if (raw === '' || raw === '-') {
    displayText.value = ''
    emit('input', undefined)
    return
  }
  const parsed = Number(sanitizeNumericInput(raw))
  if (!Number.isFinite(parsed)) {
    displayText.value = ''
    emit('input', undefined)
    return
  }
  const final = normalizeNumericValue(parsed, intN(), decM(), props.rounding)
  displayText.value = formatWithThousands(final, decM())
  emitStoredValue(final)
}
</script>
