<template>
  <!-- 
    通用金额输入组件
    支持小数位数限制、负数控制、前后缀显示等功能
  -->
  <el-input
    v-model="displayValue"
    :placeholder="placeholder"
    :disabled="disabled"
    :readonly="readonly"
    :clearable="clearable"
    @input="handleInput"
    @blur="handleBlur"
    @focus="handleFocus"
  >
    <!-- 前缀模板：根据 showPrefix 控制显示 -->
    <template v-if="showPrefix" #prefix>{{ prefix }}</template>
    <!-- 后缀模板：根据 showSuffix 控制显示 -->
    <template v-if="showSuffix" #suffix>{{ suffix }}</template>
  </el-input>
</template>

<script setup lang="ts">
/**
 * 通用金额输入组件 MoneyInput
 * 
 * 功能特性：
 * - 自动限制小数位数
 * - 可配置是否允许负数输入
 * - 支持自定义前后缀符号（如 ¥、$、% 等）
 * - 自动格式化显示，失焦时自动补全小数位
 * - 输入时自动过滤非法字符
 * 
 * 使用示例：
 * ```vue
 * <!-- 基本用法：人民币金额，2位小数 -->
 * <MoneyInput v-model="amount" />
 * 
 * <!-- 订单数量：整数，不允许负数，无符号 -->
 * <MoneyInput 
 *   v-model="quantity" 
 *   :decimal-places="0" 
 *   :allow-negative="false" 
 *   :show-prefix="false"
 * />
 * 
 * <!-- 美元金额 -->
 * <MoneyInput 
 *   v-model="usdAmount" 
 *   prefix="$" 
 *   :decimal-places="2" 
 * />
 * 
 * <!-- 百分比显示：2位小数，显示%后缀 -->
 * <MoneyInput 
 *   v-model="rate" 
 *   :decimal-places="2" 
 *   suffix="%" 
 *   :show-suffix="true" 
 *   :show-prefix="false"
 * />
 * ```
 */
import { ref, watch } from 'vue'

/**
 * Props 定义
 */
interface Props {
  /**
   * 绑定值（双向绑定）
   */
  modelValue?: number | string
  
  /**
   * 输入框占位文本
   * @default '请输入'
   */
  placeholder?: string
  
  /**
   * 是否禁用输入框
   * @default false
   */
  disabled?: boolean
  
  /**
   * 是否只读
   * @default false
   */
  readonly?: boolean
  
  /**
   * 是否显示清除按钮
   * @default true
   */
  clearable?: boolean
  
  /**
   * 小数位数
   * @default 2
   */
  decimalPlaces?: number
  
  /**
   * 是否允许输入负数
   * @default true
   */
  allowNegative?: boolean
  
  /**
   * 前缀符号（如 ¥、$）
   * @default '¥'
   */
  prefix?: string
  
  /**
   * 后缀符号（如 %、元）
   * @default ''
   */
  suffix?: string
  
  /**
   * 是否显示前缀
   * @default true
   */
  showPrefix?: boolean
  
  /**
   * 是否显示后缀
   * @default false
   */
  showSuffix?: boolean
}

/**
 * Props 默认值配置
 */
const props = withDefaults(defineProps<Props>(), {
  modelValue: undefined,
  placeholder: '请输入',
  disabled: false,
  readonly: false,
  clearable: true,
  decimalPlaces: 2,
  allowNegative: true,
  prefix: '¥',
  suffix: '',
  showPrefix: true,
  showSuffix: false
})

/**
 * 事件定义
 */
const emit = defineEmits<{
  /**
   * 值变化事件（双向绑定）
   * @param value - 新的值（number | string | undefined）
   */
  'update:modelValue': [value: number | string | undefined]
  
  /**
   * 值改变事件（失焦时触发）
   * @param value - 新的值（number | string | undefined）
   */
  'change': [value: number | string | undefined]
}>()

/**
 * 显示值（格式化后的字符串）
 */
const displayValue = ref<string>('')

/**
 * 初始化显示值
 * 将 modelValue 转换为格式化后的字符串
 */
const initDisplayValue = () => {
  if (props.modelValue !== undefined && props.modelValue !== null && props.modelValue !== '') {
    const num = Number(props.modelValue)
    if (!isNaN(num)) {
      displayValue.value = num.toFixed(props.decimalPlaces)
    } else {
      displayValue.value = ''
    }
  } else {
    displayValue.value = ''
  }
}

// 初始化显示值
initDisplayValue()

/**
 * 监听 modelValue 变化，同步更新显示值
 */
watch(() => props.modelValue, () => {
  initDisplayValue()
}, { immediate: true })

/**
 * 处理输入事件
 * 过滤非法字符，限制小数位数
 */
const handleInput = (value: string) => {
  let inputValue = value
  
  // 处理负数：如果允许负数，保留一个负号在开头
  if (props.allowNegative) {
    inputValue = inputValue.replace(/[^\d.-]/g, '')
    const negativeCount = (inputValue.match(/-/g) || []).length
    if (negativeCount > 1) {
      inputValue = '-' + inputValue.replace(/-/g, '').slice(1)
    }
    if (inputValue.indexOf('-') > 0) {
      inputValue = inputValue.replace(/-/g, '')
    }
  } else {
    // 不允许负数：只保留数字和小数点
    inputValue = inputValue.replace(/[^\d.]/g, '')
  }
  
  // 正整数模式（decimalPlaces 为 0）：不允许输入小数点
  if (props.decimalPlaces === 0) {
    inputValue = inputValue.replace(/\./g, '')
    displayValue.value = inputValue
    return
  }
  
  // 输入以小数点开头时，自动在前面补 0
  if (inputValue.startsWith('.')) {
    inputValue = '0' + inputValue
  }
  
  // 负数情况下，小数点在负号后直接输入时补 0
  if (inputValue.startsWith('-') && inputValue.length > 1 && inputValue[1] === '.') {
    inputValue = '-0' + inputValue.slice(1)
  }
  
  // 限制小数位数
  const dotIndex = inputValue.indexOf('.')
  if (dotIndex !== -1) {
    const integerPart = inputValue.slice(0, dotIndex)
    const decimalPart = inputValue.slice(dotIndex + 1)
    const limitedDecimal = decimalPart.slice(0, props.decimalPlaces)
    inputValue = integerPart + '.' + limitedDecimal
  }
  
  // 只允许一个小数点
  const dotCount = (inputValue.match(/\./g) || []).length
  if (dotCount > 1) {
    const firstDotIndex = inputValue.indexOf('.')
    inputValue = inputValue.slice(0, firstDotIndex) + inputValue.slice(firstDotIndex).replace(/\./g, '')
  }
  
  // 特殊情况处理：单独的负号或小数点
  if (inputValue === '-' || inputValue === '.') {
    displayValue.value = inputValue
    return
  }
  
  // 验证并处理负数限制
  if (inputValue !== '' && inputValue !== '-') {
    const num = Number(inputValue)
    if (!isNaN(num)) {
      if (!props.allowNegative && num < 0) {
        inputValue = Math.abs(num).toFixed(props.decimalPlaces)
      }
    }
  }
  
  displayValue.value = inputValue
}

/**
 * 处理失焦事件
 * 格式化最终值并触发事件
 */
const handleBlur = () => {
  let value = displayValue.value
  
  // 空值处理
  if (value === '' || value === '-' || value === '.') {
    displayValue.value = ''
    emit('update:modelValue', undefined)
    emit('change', undefined)
    return
  }
  
  // 验证数字有效性
  const num = Number(value)
  if (isNaN(num)) {
    displayValue.value = ''
    emit('update:modelValue', undefined)
    emit('change', undefined)
    return
  }
  
  // 负数限制处理
  if (!props.allowNegative && num < 0) {
    const absNum = Math.abs(num)
    displayValue.value = absNum.toFixed(props.decimalPlaces)
    emit('update:modelValue', absNum)
    emit('change', absNum)
    return
  }
  
  // 格式化并输出
  displayValue.value = num.toFixed(props.decimalPlaces)
  emit('update:modelValue', num)
  emit('change', num)
}

/**
 * 处理聚焦事件
 * 聚焦时移除格式化，显示原始数值
 */
const handleFocus = () => {
  if (displayValue.value !== '') {
    const num = Number(displayValue.value)
    if (!isNaN(num)) {
      displayValue.value = num.toString()
    }
  }
}
</script>

<style scoped>
/* 组件样式 */
</style>
