<template>
  <!-- 商品套餐管理主对话框 -->
  <el-dialog
    :model-value="visible"
    :title="`商品套餐管理 - ${goodName}`"
    width="900px"
    append-to-body
    @update:model-value="$emit('update:visible', $event)"
  >
    <div class="plan-management">
      <div class="plan-header">
        <el-button type="primary" @click="handleAddPlan">
          <el-icon><Plus /></el-icon>新增套餐
        </el-button>
        <el-button type="success" @click="fetchPlanList">
          <el-icon><Refresh /></el-icon>刷新
        </el-button>
      </div>

      <div v-loading="planLoading" class="plan-cards-wrap">
        <div v-if="planList.length > 0" class="plan-cards-grid">
          <div
            v-for="row in planList"
            :key="row.id"
            class="plan-card"
            :class="{ 'is-disabled': row.disable }"
          >
            <!-- 卡片头部：名称 + 状态 -->
            <div class="plan-card-header">
              <div class="plan-card-title">
                <span class="plan-card-name">{{ row.name }}</span>
                <span class="plan-card-id">#{{ row.id }}</span>
              </div>
              <el-tag :type="row.disable ? 'danger' : 'success'" size="small" effect="light">
                {{ row.disable ? '已禁用' : '启用中' }}
              </el-tag>
            </div>

            <!-- 关键数据：价格 / 库存 / 额外参数 -->
            <div class="plan-card-stats">
              <div class="plan-stat plan-stat-price" :class="{ 'is-dynamic': !isFixedPrice(row) }">
                <div class="plan-stat-label">
                  <el-icon><PriceTag /></el-icon>
                  <span>{{ isFixedPrice(row) ? '固定价格' : '参数计价' }}</span>
                </div>
                <div class="plan-stat-value">
                  <span class="price-symbol">¥</span>
                  <span class="price-amount">{{ formatPlanPrice(row) }}</span>
                </div>
              </div>
              <div class="plan-stat plan-stat-inventory" :class="getInventoryClass(row)">
                <div class="plan-stat-label">
                  <el-icon><Box /></el-icon>
                  <span>库存</span>
                </div>
                <div class="plan-stat-value">
                  <template v-if="getInventoryNum(row) > 0">
                    <span class="inventory-num">{{ getInventoryNum(row) }}</span>
                    <span class="inventory-unit">件</span>
                  </template>
                  <span v-else class="inventory-unlimited">不限</span>
                </div>
              </div>
              <div class="plan-stat plan-stat-extra">
                <div class="plan-stat-label">
                  <el-icon><Collection /></el-icon>
                  <span>额外参数</span>
                </div>
                <div class="plan-stat-value">
                  <span class="extra-num">{{ getExtraArgCount(row) }}</span>
                  <span class="extra-unit">项</span>
                </div>
              </div>
            </div>

            <!-- 参数配置：固定高度，超出滚动 -->
            <div class="plan-card-section">
              <div class="plan-card-section-title">
                <el-icon><Setting /></el-icon>
                <span>参数配置</span>
                <span v-if="row.argsParsed && row.argsParsed.length > 0" class="plan-card-section-count">
                  {{ row.argsParsed.length }}
                </span>
              </div>
              <div class="plan-card-args">
                <template v-if="row.argsParsed && row.argsParsed.length > 0">
                  <el-tag
                    v-for="(arg, index) in row.argsParsed"
                    :key="index"
                    size="small"
                    type="info"
                    effect="plain"
                    class="plan-arg-tag"
                  >
                    <span class="plan-arg-name">{{ arg.name }}</span>
                    <span class="plan-arg-sep">:</span>
                    <span class="plan-arg-value">{{ formatArgTagDisplay(arg) }}</span>
                  </el-tag>
                </template>
                <div v-else class="plan-card-empty">暂无参数</div>
              </div>
            </div>

            <!-- 元信息：排序 / 展示 / 升级 -->
            <div class="plan-card-meta">
              <span class="plan-meta-item">
                <span class="plan-meta-label">排序</span>
                <span class="plan-meta-value">{{ row.index ?? 0 }}</span>
              </span>
              <el-tag :type="row.showHome || row.show_home ? 'success' : 'info'" size="small" effect="plain">
                {{ row.showHome || row.show_home ? '首页展示' : '首页隐藏' }}
              </el-tag>
              <el-tag :type="row.canUpdate || row.can_update ? 'success' : 'info'" size="small" effect="plain">
                {{ row.canUpdate || row.can_update ? '允许升级' : '禁止升级' }}
              </el-tag>
              <el-tag v-if="(row.maxPerUser || row.max_per_user) > 0" type="warning" size="small" effect="plain">
                限购{{ row.maxPerUser || row.max_per_user }}次
              </el-tag>
            </div>

            <!-- 说明 -->
            <div v-if="row.note" class="plan-card-note" :title="row.note">
              <el-icon><InfoFilled /></el-icon>
              <span class="plan-card-note-text">{{ row.note }}</span>
            </div>

            <!-- 操作按钮 -->
            <div class="plan-card-actions">
              <el-button type="primary" link size="small" @click="handleEditPlan(row)">
                <el-icon><Edit /></el-icon>编辑
              </el-button>
              <el-button
                :type="row.disable ? 'success' : 'warning'"
                link
                size="small"
                @click="handleTogglePlanStatus(row)"
              >
                <el-icon><Switch /></el-icon>{{ row.disable ? '启用' : '禁用' }}
              </el-button>
              <el-button type="danger" link size="small" @click="handleDeletePlan(row)">
                <el-icon><Delete /></el-icon>删除
              </el-button>
            </div>
          </div>
        </div>

        <el-empty v-else-if="!planLoading" description="暂无套餐数据" :image-size="80" />
      </div>
    </div>
  </el-dialog>

  <!-- 套餐表单对话框 -->
  <el-dialog
    v-model="planFormDialogVisible"
    :title="planFormType === 'add' ? '新增套餐' : '编辑套餐'"
    width="700px"
    append-to-body
    class="plan-form-dialog"
  >
    <div class="plan-form-content">
      <el-form ref="planFormRef" :model="planForm" :rules="planFormRules" label-width="100px">
        <el-form-item label="套餐名称" prop="name">
          <el-input v-model="planForm.name" placeholder="请输入套餐名称" />
        </el-form-item>
        <el-form-item label="说明" prop="note">
          <el-input v-model="planForm.note" type="textarea" :rows="2" placeholder="请输入套餐说明" />
        </el-form-item>
        <div v-if="missingMustSpecs.length > 0" class="must-params-alert">
          <div class="must-params-alert-header">
            <el-icon><WarningFilled /></el-icon>
            <span>以下必填参数尚未配置，请将其加入「参数配置」或「额外参数」中：</span>
          </div>
          <div class="must-params-alert-tags">
            <el-tag v-for="spec in missingMustSpecs" :key="spec.id" type="danger" size="small" effect="plain">
              <span class="must-star">*</span>{{ spec.name }}
            </el-tag>
          </div>
        </div>
        <el-form-item label="参数配置" prop="args">
          <div class="args-config-container">
            <div class="args-select-row">
              <el-select v-model="selectedArgIds" multiple placeholder="请选择需要配置的参数" style="width: 100%" @change="onSelectedArgsChange">
                <el-option v-for="spec in planSpecList" :key="spec.id" :label="spec.must ? `* ${spec.name}` : spec.name" :value="spec.id" />
              </el-select>
            </div>
            <div v-if="selectedArgSpecs.length > 0" class="args-selector">
              <div v-for="spec in selectedArgSpecs" :key="spec.id" class="spec-item">
                <div class="spec-label">
                  <span v-if="spec.must" class="must-star">*</span>{{ spec.name }}
                </div>
                <div class="spec-values">
                  <template v-if="spec.type === 'select' && spec.attrs && spec.attrs.length > 0">
                    <el-radio-group v-model="selectedArgs[spec.id]" size="small" @change="updateArgsJson">
                      <el-radio-button v-for="attr in spec.attrs" :key="attr.id" :value="attr.id">{{ attr.name }}</el-radio-button>
                    </el-radio-group>
                  </template>
                  <template v-else-if="spec.type === 'number'">
                    <div class="number-input-wrapper">
                      <el-input-number
                        v-model="displayValues[spec.id]"
                        :min="getSpecDisplayMin(spec)"
                        :max="getSpecDisplayMax(spec)"
                        :step="getSpecDisplayStep(spec)"
                        :step-strictly="true"
                        size="small"
                        @change="onNumberDisplayChange(spec)"
                      />
                      <el-select
                        v-if="hasUnit(spec)"
                        :model-value="displayUnits[spec.id]"
                        size="small"
                        style="width: 90px"
                        @change="(newUnit) => onPlanUnitChange(spec, newUnit)"
                      >
                        <el-option v-for="u in getParamUnits(spec)" :key="u" :label="u" :value="u" />
                      </el-select>
                      <span class="number-range">({{ spec.min ?? 0 }} - {{ spec.max ?? 0 }}
                        <template v-if="hasUnit(spec)">{{ getBaseUnit(getArgKey(spec)) }}</template>，步长: {{ spec.step ?? 1 }})</span>
                    </div>
                    <div v-if="spec.attrs && spec.attrs.length > 0 && selectedArgs[spec.id]" class="matched-attr-info">
                      <el-tag type="success" size="small">匹配区间: {{ getMatchedAttrName(spec, selectedArgs[spec.id]) }}</el-tag>
                    </div>
                    <div v-if="getArgKey(spec) === 'system_size'" class="system-size-tip">
                      <el-tag :type="isSystemSizeTooSmall(spec) ? 'danger' : 'warning'" size="small" effect="light">
                        <el-icon style="vertical-align: -2px; margin-right: 2px;"><WarningFilled /></el-icon>
                        {{ isSystemSizeTooSmall(spec) ? '当前系统盘小于30GB，Windows镜像可能无法正常运行' : 'Windows系统镜像建议系统盘不低于30GB' }}
                      </el-tag>
                    </div>
                  </template>
                  <template v-else>
                    <el-input v-model="selectedArgs[spec.id]" placeholder="请输入值" size="small" style="width: 200px" @input="updateArgsJson" />
                  </template>
                </div>
              </div>
            </div>
            <el-empty v-else-if="planSpecList.length > 0" description="请先选择需要配置的参数" :image-size="60" />
            <el-empty v-else description="暂无参数配置，请先为商品添加参数" :image-size="60" />
            <div class="args-actions" v-if="selectedArgSpecs.length > 0">
              <el-button type="info" plain size="small" @click="showArgsPreview = true">
                <el-icon><View /></el-icon>查看配置JSON
              </el-button>
              <el-button type="primary" plain size="small" @click="handleCopyArgsJson">
                <el-icon><CopyDocument /></el-icon>复制JSON
              </el-button>
              <el-button type="success" plain size="small" @click="handlePasteArgsJson">
                <el-icon><DocumentAdd /></el-icon>粘贴JSON
              </el-button>
              <el-button type="warning" plain size="small" @click="clearArgsSelection">
                <el-icon><Delete /></el-icon>清空选择
              </el-button>
            </div>
          </div>
        </el-form-item>
        <el-form-item label="额外参数">
          <div class="args-config-container">
            <div class="form-tip" style="margin-bottom: 8px;">选择参数配置中未选择的参数作为额外参数</div>
            <el-select v-model="selectedExtraArgIds" multiple placeholder="请选择额外参数" style="width: 100%" @change="onSelectedExtraArgsChange">
              <el-option v-for="spec in extraSpecList" :key="spec.id" :label="spec.must ? `* ${spec.name} (ID: ${spec.id})` : `${spec.name} (ID: ${spec.id})`" :value="spec.id" />
            </el-select>
            <el-empty v-if="extraSpecList.length === 0" description="所有参数已在参数配置中选择" :image-size="40" />
          </div>
        </el-form-item>
        <el-form-item label="库存数量" prop="inventory">
          <el-input-number v-model="planForm.inventory" :min="0" style="width: 100%" placeholder="库存数量" />
          <div class="form-tip">0 表示没有库存</div>
        </el-form-item>
        <el-form-item label="启用固定价格" prop="enable_fixed_price">
          <el-switch v-model="planForm.enable_fixed_price" :active-value="true" :inactive-value="false" active-text="启用" inactive-text="禁用" :loading="fixedPriceLoading" @change="handleFixedPriceChange" />
          <div class="form-tip">启用后套餐价格将使用固定价格，不再根据参数计算</div>
        </el-form-item>
        <el-form-item label="固定价格" prop="fixed_price" v-if="planForm.enable_fixed_price === true">
          <div class="unit-input-row">
            <el-input-number v-model="planForm.fixed_price" :min="0" :precision="2" :step="0.01" style="flex:1" placeholder="请输入固定价格(元)" />
            <span class="unit-text">元</span>
          </div>
        </el-form-item>
        <el-form-item label="续费固定价格" prop="renew_fixed_price" v-if="planForm.enable_fixed_price === true">
          <div class="unit-input-row">
            <el-input-number v-model="planForm.renew_fixed_price" :min="0" :precision="2" :step="0.01" style="flex:1" placeholder="0 表示沿用固定价格" />
            <span class="unit-text">元</span>
          </div>
          <div class="form-tip">续费时使用的固定价格，为 0 时沿用上方固定价格</div>
        </el-form-item>
        <el-form-item label="排序索引" prop="index">
          <el-input-number v-model="planForm.index" :min="0" style="width: 100%" />
        </el-form-item>
        <el-form-item label="状态" prop="disable" v-if="planFormType === 'edit'">
          <el-radio-group v-model="planForm.disable">
            <el-radio :value="false">启用</el-radio>
            <el-radio :value="true">禁用</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="首页展示" prop="show_home">
          <el-switch v-model="planForm.show_home" active-text="展示" inactive-text="不展示" />
          <div class="form-tip">控制商品套餐是否在首页显示</div>
        </el-form-item>
        <el-form-item label="允许升级" prop="can_update">
          <el-switch v-model="planForm.can_update" active-text="允许" inactive-text="不允许" />
          <div class="form-tip">控制用户是否可以升级到此套餐</div>
        </el-form-item>
        <el-form-item label="购买限制" prop="max_per_user">
          <el-input-number v-model="planForm.max_per_user" :min="0" placeholder="单用户最大购买数量" style="width: 100%" />
          <div class="form-tip">限制单个用户对该套餐的最大购买数量，0 表示不限制</div>
        </el-form-item>
      </el-form>
    </div>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="planFormDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitPlanForm">确定</el-button>
      </div>
    </template>
  </el-dialog>

  <!-- 手动粘贴JSON对话框 -->
  <el-dialog v-model="showPasteDialog" title="粘贴JSON" width="500px" append-to-body>
    <el-input v-model="pasteJsonText" type="textarea" :rows="8" placeholder="请将JSON粘贴到此处" />
    <template #footer>
      <el-button @click="showPasteDialog = false">取消</el-button>
      <el-button type="primary" @click="doPasteFromText">确定导入</el-button>
    </template>
  </el-dialog>

  <!-- 参数配置预览对话框 -->
  <el-dialog v-model="showArgsPreview" title="参数配置预览" width="500px" append-to-body>
    <div class="args-preview">
      <div class="preview-header">
        <span>已选择 {{ Object.keys(selectedArgs).filter(k => selectedArgs[k] !== undefined && selectedArgs[k] !== '').length }} 个参数</span>
        <el-tag :type="isArgsValid ? 'success' : 'warning'" size="small">{{ isArgsValid ? '配置有效' : '部分参数未选择' }}</el-tag>
      </div>
      <el-divider />
      <div class="preview-list">
        <div v-for="spec in planSpecList" :key="spec.id" class="preview-item">
          <span class="preview-label">{{ spec.name }}:</span>
          <span class="preview-value" :class="{ 'not-selected': !getSelectedValueDisplay(spec) }">{{ getSelectedValueDisplay(spec) || '未选择' }}</span>
        </div>
      </div>
      <el-divider content-position="left">JSON 数据</el-divider>
      <pre class="json-preview">{{ formatArgsJsonPreview() }}</pre>
    </div>
    <template #footer>
      <el-button @click="showArgsPreview = false">关闭</el-button>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, reactive, computed, watch, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Refresh, Delete, View, CopyDocument, DocumentAdd, WarningFilled, Setting, InfoFilled, Edit, Switch, PriceTag, Box, Collection } from '@element-plus/icons-vue'
import {
  getProductParameterList,
  getProductPlanList,
  getProductPlanDetail,
  createProductPlan,
  updateProductPlan,
  deleteProductPlan,
  disableProductPlan,
  enableProductPlan,
  disablePlanFixedPrice,
  enablePlanFixedPrice
} from '@/api/admin/product'
import {
  hasUnit, getArgKey, getBaseUnit, getParamUnits, getParamDefaultUnit,
  toBaseUnit, fromBaseUnit, formatValueWithUnit
} from '@/utils/dynamicUnit'

const props = defineProps({
  visible: { type: Boolean, default: false },
  goodId: { type: Number, default: null },
  goodName: { type: String, default: '' }
})
const emit = defineEmits(['update:visible'])

const planLoading = ref(false)
const planList = ref([])
const fixedPriceLoading = ref(false)

const planFormDialogVisible = ref(false)
const planFormType = ref('add')
const planFormRef = ref(null)
const planForm = reactive({
  plan_id: undefined,
  name: '',
  note: '',
  args: '',
  extra_arg_ids: '',
  extra_arg_ids_array: [],
  inventory: 0,
  fixed_price: 0,
  enable_fixed_price: false,
  index: 0,
  disable: false,
  show_home: false,
  can_update: false,
  max_per_user: 0,
  renew_fixed_price: 0
})
const planFormRules = {
  name: [{ required: true, message: '请输入套餐名称', trigger: 'blur' }]
}

const planSpecList = ref([])
const selectedArgIds = ref([])
const selectedArgs = reactive({})
const displayValues = reactive({})
const displayUnits = reactive({})
const showArgsPreview = ref(false)
const showPasteDialog = ref(false)
const pasteJsonText = ref('')
const selectedExtraArgIds = ref([])

const selectedArgSpecs = computed(() => planSpecList.value.filter(spec => selectedArgIds.value.includes(spec.id)))
const extraSpecList = computed(() => planSpecList.value.filter(spec => !selectedArgIds.value.includes(spec.id)))
const missingMustSpecs = computed(() => {
  const allSelectedIds = [...selectedArgIds.value, ...selectedExtraArgIds.value]
  return planSpecList.value.filter(s => s.must && !allSelectedIds.includes(s.id))
})

const getSpecDisplayMin = (spec) => {
  if (!hasUnit(spec)) return spec.min ?? 0
  const argKey = getArgKey(spec)
  const unit = displayUnits[spec.id]
  return unit ? fromBaseUnit(spec.min ?? 0, unit, argKey) : (spec.min ?? 0)
}
const getSpecDisplayMax = (spec) => {
  if (!hasUnit(spec)) return spec.max ?? 0
  const argKey = getArgKey(spec)
  const unit = displayUnits[spec.id]
  return unit ? fromBaseUnit(spec.max ?? 0, unit, argKey) : (spec.max ?? 0)
}
const getSpecDisplayStep = (spec) => {
  if (!hasUnit(spec)) return spec.step ?? 1
  const argKey = getArgKey(spec)
  const unit = displayUnits[spec.id]
  if (!unit) return spec.step ?? 1
  const converted = fromBaseUnit(spec.step ?? 1, unit, argKey)
  return converted > 0 ? converted : 1
}

const onNumberDisplayChange = (spec) => {
  if (hasUnit(spec)) {
    const argKey = getArgKey(spec)
    const unit = displayUnits[spec.id]
    selectedArgs[spec.id] = Math.round(toBaseUnit(displayValues[spec.id] || 0, unit, argKey))
  } else {
    selectedArgs[spec.id] = displayValues[spec.id]
  }
  updateArgsJson()
}

const onPlanUnitChange = (spec, newUnit) => {
  const argKey = getArgKey(spec)
  const oldUnit = displayUnits[spec.id]
  const oldDisplay = displayValues[spec.id] || 0
  const baseValue = oldUnit ? toBaseUnit(oldDisplay, oldUnit, argKey) : oldDisplay
  displayUnits[spec.id] = newUnit
  displayValues[spec.id] = fromBaseUnit(baseValue, newUnit, argKey)
  selectedArgs[spec.id] = Math.round(baseValue)
  updateArgsJson()
}

const isFixedPrice = (row) => {
  return !!(row.enableFixedPrice ?? row.enable_fixed_price)
}

const formatPlanPrice = (row) => {
  const raw = isFixedPrice(row)
    ? (row.fixedPrice ?? row.fixed_price ?? 0)
    : (row.argsPrice ?? row.args_price ?? 0)
  return (Number(raw) / 100).toFixed(2)
}

const getInventoryNum = (row) => {
  return Number(row.inventory ?? 0) || 0
}

const getInventoryClass = (row) => {
  const n = getInventoryNum(row)
  if (n === 0) return 'is-unlimited'
  if (n <= 5) return 'is-low'
  return ''
}

const getExtraArgCount = (row) => {
  const extraArgs = row.extraArgs || row.extra_args
  if (Array.isArray(extraArgs)) return extraArgs.length
  const ids = row.extraArgIds || row.extra_arg_ids
  if (Array.isArray(ids)) return ids.length
  if (typeof ids === 'string' && ids.trim()) {
    return ids.split(',').filter(Boolean).length
  }
  return 0
}

const formatArgTagDisplay = (arg) => {
  if (arg.number !== undefined && arg.number !== 0) {
    const spec = planSpecList.value.find(s => s.id === arg.arg_id)
    if (spec && hasUnit(spec)) {
      const argKey = getArgKey(spec)
      const displayUnit = getParamDefaultUnit(spec)
      const displayVal = fromBaseUnit(arg.number, displayUnit, argKey)
      return formatValueWithUnit(displayVal, displayUnit)
    }
    return String(arg.number)
  }
  return arg.value || '-'
}

const parseArgs = (argsStr) => {
  if (!argsStr) return []
  try { const parsed = JSON.parse(argsStr); return Array.isArray(parsed) ? parsed : [] }
  catch (e) { return [] }
}

const fetchPlanSpecList = async () => {
  if (!props.goodId) return
  try {
    const res = await getProductParameterList({ good_id: props.goodId })
    if (res.data.code === 200) planSpecList.value = res.data.data || []
  } catch (error) { planSpecList.value = [] }
}

const onSelectedArgsChange = () => {
  for (const key in selectedArgs) { if (!selectedArgIds.value.includes(Number(key))) { delete selectedArgs[key]; delete displayValues[key]; delete displayUnits[key] } }
  selectedExtraArgIds.value = selectedExtraArgIds.value.filter(id => !selectedArgIds.value.includes(id))
  for (const specId of selectedArgIds.value) {
    const spec = planSpecList.value.find(s => s.id === specId)
    if (spec && spec.type === 'number' && hasUnit(spec) && !displayUnits[specId]) {
      displayUnits[specId] = getParamDefaultUnit(spec)
    }
  }
  updateArgsJson()
  updateExtraArgIds()
}

const onSelectedExtraArgsChange = () => { updateExtraArgIds() }
const updateExtraArgIds = () => { planForm.extra_arg_ids = selectedExtraArgIds.value.join(','); planForm.extra_arg_ids_array = [...selectedExtraArgIds.value] }

const updateArgsJson = () => {
  const argsArray = []
  for (const specId of selectedArgIds.value) {
    const spec = planSpecList.value.find(s => s.id === specId)
    if (!spec) continue
    const selectedValue = selectedArgs[spec.id]
    if (selectedValue === undefined || selectedValue === '') continue
    if (spec.type === 'select') {
      const attrObj = spec.attrs?.find(a => a.id === selectedValue)
      if (attrObj) argsArray.push({ arg_id: spec.id, name: spec.name, attr_id: attrObj.id, value: attrObj.value || '', key: getArgKey(spec) || undefined })
    } else if (spec.type === 'number') {
      const numValue = Number(selectedValue)
      const matchedAttr = findMatchingNumberAttr(spec, numValue)
      argsArray.push({ arg_id: spec.id, name: spec.name, attr_id: matchedAttr ? matchedAttr.id : 0, number: numValue, key: getArgKey(spec) || undefined })
    } else {
      argsArray.push({ arg_id: spec.id, name: spec.name, attr_id: 0, value: String(selectedValue), key: getArgKey(spec) || undefined })
    }
  }
  planForm.args = argsArray.length > 0 ? JSON.stringify(argsArray) : ''
}

const findMatchingNumberAttr = (spec, numValue) => {
  if (!spec.attrs || spec.attrs.length === 0) return null
  const sortedAttrs = [...spec.attrs].sort((a, b) => (a.index || 0) - (b.index || 0))
  for (const attr of sortedAttrs) {
    const rangeVal = attr.range ?? attr.phase ?? 0
    const rangeType = attr.rangeType || 'before'
    if (rangeType === 'before' && numValue <= rangeVal) return attr
    else if (rangeType === 'after' && numValue >= rangeVal) return attr
    else if (rangeType === 'equal' && numValue === rangeVal) return attr
  }
  return sortedAttrs[sortedAttrs.length - 1]
}

const isSystemSizeTooSmall = (spec) => {
  const val = displayValues[spec.id]
  if (val === undefined || val === null) return false
  const unit = displayUnits[spec.id]
  if (unit === 'TB') return val * 1024 < 30
  return val < 30
}

const getMatchedAttrName = (spec, numValue) => {
  const matchedAttr = findMatchingNumberAttr(spec, Number(numValue))
  if (matchedAttr) { const priceText = matchedAttr.price ? ` (+¥${(matchedAttr.price / 100).toFixed(2)})` : ''; return `${matchedAttr.name}${priceText}` }
  return '无匹配区间'
}

const clearArgsSelection = () => {
  selectedArgIds.value = []
  for (const key in selectedArgs) delete selectedArgs[key]
  for (const key in displayValues) delete displayValues[key]
  for (const key in displayUnits) delete displayUnits[key]
  selectedExtraArgIds.value = []
  planForm.args = ''
  planForm.extra_arg_ids = ''
  planForm.extra_arg_ids_array = []
}

const getSelectedValueDisplay = (spec) => {
  const selectedValue = selectedArgs[spec.id]
  if (selectedValue === undefined || selectedValue === '') return null
  if (spec.type === 'select') { const attrObj = spec.attrs?.find(a => a.id === selectedValue); return attrObj ? attrObj.name : null }
  if (hasUnit(spec)) {
    const argKey = getArgKey(spec)
    const unit = displayUnits[spec.id] || getParamDefaultUnit(spec)
    const displayVal = fromBaseUnit(Number(selectedValue), unit, argKey)
    return formatValueWithUnit(displayVal, unit)
  }
  return String(selectedValue)
}

const isArgsValid = computed(() => Object.keys(selectedArgs).some(k => selectedArgs[k] !== undefined && selectedArgs[k] !== ''))

const formatArgsJsonPreview = () => {
  if (!planForm.args) return '[]'
  try { return JSON.stringify(JSON.parse(planForm.args), null, 2) } catch { return planForm.args }
}

const handleCopyArgsJson = async () => {
  if (!planForm.args) { ElMessage.warning('暂无参数配置可复制'); return }
  try {
    await navigator.clipboard.writeText(planForm.args)
    ElMessage.success('已复制参数配置JSON到剪贴板')
  } catch {
    const textarea = document.createElement('textarea')
    textarea.value = planForm.args
    document.body.appendChild(textarea)
    textarea.select()
    document.execCommand('copy')
    document.body.removeChild(textarea)
    ElMessage.success('已复制参数配置JSON到剪贴板')
  }
}

const handlePasteArgsJson = async () => {
  let clipText = ''
  try {
    clipText = await navigator.clipboard.readText()
  } catch {
    pasteJsonText.value = ''
    showPasteDialog.value = true
    return
  }
  if (!clipText || !clipText.trim()) { ElMessage.warning('剪贴板为空'); return }
  applyPastedJson(clipText)
}

const doPasteFromText = () => {
  const text = pasteJsonText.value
  if (!text || !text.trim()) { ElMessage.warning('请输入JSON内容'); return }
  showPasteDialog.value = false
  applyPastedJson(text)
}

const applyPastedJson = (jsonText) => {
  let pastedArgs
  try {
    pastedArgs = JSON.parse(jsonText)
    if (!Array.isArray(pastedArgs)) { ElMessage.error('JSON格式错误，需要数组格式'); return }
  } catch {
    ElMessage.error('JSON解析失败，请检查格式')
    return
  }

  clearArgsSelection()
  const matchedIds = []
  for (const arg of pastedArgs) {
    let spec = null
    if (arg.key) {
      spec = planSpecList.value.find(s => getArgKey(s) === arg.key)
    }
    if (!spec && arg.name) {
      spec = planSpecList.value.find(s => s.name === arg.name)
    }
    if (!spec && arg.arg_id) {
      spec = planSpecList.value.find(s => s.id === arg.arg_id)
    }
    if (!spec) continue

    matchedIds.push(spec.id)
    if (spec.type === 'select') {
      if (arg.attr_id) {
        const attrObj = spec.attrs?.find(a => a.id === arg.attr_id)
        if (attrObj) selectedArgs[spec.id] = attrObj.id
        else if (arg.value) {
          const byVal = spec.attrs?.find(a => a.value === arg.value || a.name === arg.value)
          if (byVal) selectedArgs[spec.id] = byVal.id
        }
      } else if (arg.value) {
        const byVal = spec.attrs?.find(a => a.value === arg.value || a.name === arg.value)
        if (byVal) selectedArgs[spec.id] = byVal.id
      }
    } else if (spec.type === 'number') {
      const numVal = Number(arg.number !== undefined ? arg.number : arg.value)
      selectedArgs[spec.id] = numVal
      if (hasUnit(spec)) {
        const unit = getParamDefaultUnit(spec)
        displayUnits[spec.id] = unit
        displayValues[spec.id] = fromBaseUnit(numVal, unit, getArgKey(spec))
      } else {
        displayValues[spec.id] = numVal
      }
    } else {
      selectedArgs[spec.id] = arg.value || ''
    }
  }
  selectedArgIds.value = matchedIds
  updateArgsJson()
  updateExtraArgIds()
  ElMessage.success(`已从JSON导入 ${matchedIds.length} 个参数配置`)
}

const initSelectedArgsFromJson = (argsJson, extraArgIds = []) => {
  clearArgsSelection()
  const argsParamIds = []
  if (argsJson) {
    try {
      const argsArray = typeof argsJson === 'string' ? JSON.parse(argsJson) : argsJson
      if (Array.isArray(argsArray)) {
        for (const arg of argsArray) {
          const spec = planSpecList.value.find(s => s.id === arg.arg_id)
          if (!spec) continue
          argsParamIds.push(spec.id)
          if (spec.type === 'select') {
            if (arg.attr_id) selectedArgs[spec.id] = arg.attr_id
            else if (arg.id) selectedArgs[spec.id] = arg.id
            else { const attrObj = spec.attrs?.find(a => a.value === arg.value || a.name === arg.name); if (attrObj) selectedArgs[spec.id] = attrObj.id }
          } else if (spec.type === 'number') {
            const numVal = Number(arg.number !== undefined ? arg.number : arg.value)
            selectedArgs[spec.id] = numVal
            if (hasUnit(spec)) {
              const unit = getParamDefaultUnit(spec)
              displayUnits[spec.id] = unit
              displayValues[spec.id] = fromBaseUnit(numVal, unit, getArgKey(spec))
            } else {
              displayValues[spec.id] = numVal
            }
          } else {
            selectedArgs[spec.id] = arg.value
          }
        }
      }
    } catch (e) { console.error('解析args失败:', e) }
  }
  selectedArgIds.value = argsParamIds
  if (extraArgIds && extraArgIds.length > 0) selectedExtraArgIds.value = extraArgIds.filter(id => !argsParamIds.includes(id))
  updateArgsJson()
  updateExtraArgIds()
}

const fetchPlanList = async () => {
  if (!props.goodId) return
  planLoading.value = true
  try {
    const res = await getProductPlanList({ good_id: String(props.goodId) })
    if (res.data.code === 200) {
      let data = res.data.data
      if (Array.isArray(data)) planList.value = data.map(item => ({ ...item, argsParsed: parseArgs(item.args) }))
      else if (data && data.list) planList.value = data.list.map(item => ({ ...item, argsParsed: parseArgs(item.args) }))
      else if (data && data.data) planList.value = data.data.map(item => ({ ...item, argsParsed: parseArgs(item.args) }))
      else planList.value = []
    }
  } catch (error) { ElMessage.error('获取套餐列表失败') }
  finally { planLoading.value = false }
}

const handleAddPlan = async () => {
  planFormType.value = 'add'
  await fetchPlanSpecList()
  clearArgsSelection()
  selectedArgIds.value = planSpecList.value.map(spec => spec.id)
  for (const spec of planSpecList.value) {
    if (spec.type === 'number' && hasUnit(spec)) {
      displayUnits[spec.id] = getParamDefaultUnit(spec)
    }
  }
  Object.assign(planForm, { plan_id: undefined, name: '', note: '', args: '', extra_arg_ids: '', extra_arg_ids_array: [], inventory: 0, fixed_price: 0, enable_fixed_price: false, index: 0, disable: false, show_home: false, can_update: false, max_per_user: 0 })
  planFormDialogVisible.value = true
  nextTick(() => { planFormRef.value?.resetFields() })
}

const handleEditPlan = async (row) => {
  planFormType.value = 'edit'
  await fetchPlanSpecList()
  try {
    const res = await getProductPlanDetail({ good_id: String(props.goodId), plan_id: String(row.id) })
    if (res.data.code === 200) {
      const data = res.data.data
      let extraArgIdsArray = []
      const extraArgIdsRaw = data.extraArgIds || data.extra_arg_ids
      const extraArgsRaw = data.extraArgs || data.extra_args
      if (extraArgIdsRaw) {
        if (Array.isArray(extraArgIdsRaw)) extraArgIdsArray = extraArgIdsRaw.map(Number)
        else if (typeof extraArgIdsRaw === 'string' && extraArgIdsRaw.trim()) extraArgIdsArray = extraArgIdsRaw.split(',').filter(Boolean).map(Number)
      } else if (extraArgsRaw && Array.isArray(extraArgsRaw)) {
        extraArgIdsArray = extraArgsRaw.map(arg => arg.id || arg.arg_id || arg.argId).filter(Boolean).map(Number)
      }
      Object.assign(planForm, {
        plan_id: data.id, name: data.name || '', note: data.note || '', args: data.args || '',
        extra_arg_ids: extraArgIdsArray.join(','), extra_arg_ids_array: extraArgIdsArray,
        inventory: data.inventory || 0, fixed_price: ((data.fixedPrice || data.fixed_price || 0) / 100).toFixed(2) * 1,
        enable_fixed_price: !!(data.enableFixedPrice || data.enable_fixed_price),
        index: data.index || 0, disable: data.disable || false, show_home: !!(data.showHome || data.show_home),
        can_update: !!(data.canUpdate || data.can_update),
        max_per_user: data.maxPerUser ?? data.max_per_user ?? 0,
        renew_fixed_price: ((data.renewFixedPrice ?? data.renew_fixed_price ?? 0) / 100).toFixed(2) * 1
      })
      initSelectedArgsFromJson(data.args, extraArgIdsArray)
      planFormDialogVisible.value = true
    }
  } catch (error) { ElMessage.error('获取套餐详情失败') }
}

const handleDeletePlan = (row) => {
  ElMessageBox.confirm(`确认删除套餐 ${row.name} 吗？`, '警告', {
    confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
  }).then(async () => {
    try {
      const res = await deleteProductPlan({ good_id: String(props.goodId), plan_id: String(row.id) })
      if (res.data.code === 200) { ElMessage.success('删除成功'); fetchPlanList() }
    } catch (error) { ElMessage.error('删除失败') }
  }).catch(() => {})
}

const handleTogglePlanStatus = async (row) => {
  try {
    const res = row.disable
      ? await enableProductPlan({ good_id: String(props.goodId), plan_id: String(row.id) })
      : await disableProductPlan({ good_id: String(props.goodId), plan_id: String(row.id) })
    if (res.data.code === 200) { ElMessage.success(row.disable ? '启用成功' : '禁用成功'); fetchPlanList() }
  } catch (error) { ElMessage.error('操作失败') }
}

const handleFixedPriceChange = async (value) => {
  if (planFormType.value !== 'edit' || !planForm.plan_id) return
  fixedPriceLoading.value = true
  try {
    const data = { good_id: String(props.goodId), plan_id: String(planForm.plan_id) }
    const res = value === true ? await enablePlanFixedPrice(data) : await disablePlanFixedPrice(data)
    if (res.data.code === 200) ElMessage.success(value ? '已启用固定价格' : '已禁用固定价格')
    else { planForm.enable_fixed_price = !value; ElMessage.error(res.data.message || '操作失败') }
  } catch (error) { planForm.enable_fixed_price = !value; ElMessage.error('操作失败') }
  finally { fixedPriceLoading.value = false }
}

const submitPlanForm = () => {
  planFormRef.value?.validate(async (valid) => {
    if (valid) {
      const mustSpecs = planSpecList.value.filter(s => s.must)
      const allSelectedIds = [...selectedArgIds.value, ...selectedExtraArgIds.value]
      const missingMust = mustSpecs.filter(s => !allSelectedIds.includes(s.id))
      if (missingMust.length > 0) {
        ElMessage.warning(`以下必选参数未配置：${missingMust.map(s => s.name).join('、')}，请在参数配置或额外参数中选择`)
        return
      }
      try {
        const extraArgIdsStr = selectedExtraArgIds.value.join(',')
        const submitData = {
          good_id: String(props.goodId), name: planForm.name, note: planForm.note || '',
          args: planForm.args || '', extra_arg_ids: extraArgIdsStr || planForm.extra_arg_ids || '',
          inventory: Number(planForm.inventory) || 0, fixed_price: Math.round(Number(planForm.fixed_price) * 100) || 0,
          index: Number(planForm.index) || 0, show_home: planForm.show_home === true,
          can_update: planForm.can_update === true,
          max_per_user: Number(planForm.max_per_user) || 0,
          renew_fixed_price: Math.round(Number(planForm.renew_fixed_price) * 100) || 0
        }
        if (planFormType.value === 'add') submitData.enable_fixed_price = planForm.enable_fixed_price === true
        let res
        if (planFormType.value === 'add') res = await createProductPlan(submitData)
        else { submitData.plan_id = String(planForm.plan_id); submitData.disable = planForm.disable; res = await updateProductPlan(submitData) }
        if (res.data.code === 200) { ElMessage.success(planFormType.value === 'add' ? '新增成功' : '修改成功'); planFormDialogVisible.value = false; fetchPlanList() }
        else ElMessage.error(res.data.message || '操作失败')
      } catch (error) { ElMessage.error(error.response?.data?.message || '操作失败') }
    }
  })
}

watch(() => props.visible, (val) => {
  if (val && props.goodId) fetchPlanList()
})
</script>

<style scoped>
.text-muted { color: #c0c4cc; font-size: 12px; }
.dialog-footer { display: flex; justify-content: flex-end; gap: 12px; padding: 0; }
.form-tip { font-size: 12px; color: #909399; margin-top: 4px; }
.plan-management { padding: 0; }
.plan-header { display: flex; gap: 12px; margin-bottom: 16px; }
.args-list { display: flex; flex-wrap: wrap; gap: 4px; }

/* ===== 套餐卡片列表 ===== */
.plan-cards-wrap {
  min-height: 160px;
}
.plan-cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 16px;
}
.plan-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid #e4e7ed;
  border-radius: 10px;
  padding: 14px 16px;
  transition: box-shadow 0.2s, border-color 0.2s, transform 0.2s;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  position: relative;
}
.plan-card:hover {
  border-color: #409eff;
  box-shadow: 0 4px 16px rgba(64, 158, 255, 0.12);
  transform: translateY(-1px);
}
.plan-card.is-disabled {
  background: #fafafa;
  opacity: 0.85;
}
.plan-card.is-disabled::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 10px;
  background: repeating-linear-gradient(
    -45deg,
    transparent 0 10px,
    rgba(245, 108, 108, 0.04) 10px 11px
  );
  pointer-events: none;
}

.plan-card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 1px dashed #ebeef5;
}
.plan-card-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  flex: 1;
}
.plan-card-name {
  font-size: 15px;
  font-weight: 600;
  color: #2c3e50;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.plan-card-id {
  font-size: 12px;
  color: #909399;
  font-weight: 400;
  flex-shrink: 0;
}

.plan-card-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-bottom: 12px;
}
.plan-stat {
  position: relative;
  padding: 8px 10px;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  background: #fafbfc;
  display: flex;
  flex-direction: column;
  gap: 4px;
  overflow: hidden;
}
.plan-stat-label {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  color: #909399;
  line-height: 1;
}
.plan-stat-label .el-icon {
  font-size: 12px;
}
.plan-stat-value {
  display: flex;
  align-items: baseline;
  gap: 2px;
  font-weight: 600;
  color: #303133;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
}

.plan-stat-price {
  background: linear-gradient(135deg, #fff5f5 0%, #ffeded 100%);
  border-color: #fcdcdc;
}
.plan-stat-price .plan-stat-label {
  color: #e47470;
}
.plan-stat-price .price-symbol {
  font-size: 13px;
  color: #f56c6c;
  margin-right: 1px;
}
.plan-stat-price .price-amount {
  font-size: 18px;
  color: #f56c6c;
  font-weight: 700;
}
.plan-stat-price.is-dynamic {
  background: linear-gradient(135deg, #f0f7ff 0%, #e6f2ff 100%);
  border-color: #c6e2ff;
}
.plan-stat-price.is-dynamic .plan-stat-label {
  color: #409eff;
}
.plan-stat-price.is-dynamic .price-symbol,
.plan-stat-price.is-dynamic .price-amount {
  color: #409eff;
}

.plan-stat-inventory .inventory-num {
  font-size: 18px;
  font-weight: 700;
  color: #67c23a;
}
.plan-stat-inventory .inventory-unit {
  font-size: 11px;
  color: #909399;
  font-weight: 400;
}
.plan-stat-inventory.is-low {
  background: linear-gradient(135deg, #fdf6ec 0%, #faecd8 100%);
  border-color: #f5dab1;
}
.plan-stat-inventory.is-low .inventory-num {
  color: #e6a23c;
}
.plan-stat-inventory.is-low .plan-stat-label {
  color: #e6a23c;
}
.plan-stat-inventory.is-unlimited {
  background: linear-gradient(135deg, #f4f4f5 0%, #ebeef5 100%);
}
.plan-stat-inventory .inventory-unlimited {
  font-size: 14px;
  color: #909399;
  font-weight: 600;
}

.plan-stat-extra .extra-num {
  font-size: 18px;
  font-weight: 700;
  color: #409eff;
}
.plan-stat-extra .extra-unit {
  font-size: 11px;
  color: #909399;
  font-weight: 400;
}

.plan-card-section {
  margin-bottom: 12px;
}
.plan-card-section-title {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: #606266;
  font-weight: 500;
  margin-bottom: 8px;
}
.plan-card-section-title .el-icon {
  color: #409eff;
  font-size: 14px;
}
.plan-card-section-count {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  border-radius: 9px;
  background: #ecf5ff;
  color: #409eff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
}
.plan-card-args {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  height: 108px;
  overflow-y: auto;
  padding: 8px;
  border: 1px solid #f0f2f5;
  border-radius: 6px;
  background: #fafbfc;
  align-content: flex-start;
}
.plan-card-args::-webkit-scrollbar {
  width: 6px;
}
.plan-card-args::-webkit-scrollbar-track {
  background: transparent;
}
.plan-card-args::-webkit-scrollbar-thumb {
  background-color: rgba(144, 147, 153, 0.25);
  border-radius: 3px;
}
.plan-card-args::-webkit-scrollbar-thumb:hover {
  background-color: rgba(144, 147, 153, 0.45);
}
.plan-card-empty {
  width: 100%;
  text-align: center;
  color: #c0c4cc;
  font-size: 12px;
  padding: 20px 0;
}
.plan-arg-tag {
  max-width: 100%;
  overflow: hidden;
}
.plan-arg-tag :deep(.el-tag__content) {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  max-width: 100%;
}
.plan-arg-name {
  color: #909399;
}
.plan-arg-sep {
  color: #c0c4cc;
  margin: 0 2px;
}
.plan-arg-value {
  color: #303133;
  font-weight: 500;
}

.plan-card-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  font-size: 12px;
  margin-bottom: 10px;
}
.plan-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.plan-meta-label {
  color: #909399;
}
.plan-meta-value {
  color: #303133;
  font-weight: 500;
}

.plan-card-note {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font-size: 12px;
  color: #606266;
  background: #fffbea;
  border: 1px solid #ffe8a6;
  border-radius: 6px;
  padding: 6px 8px;
  margin-bottom: 10px;
  line-height: 1.5;
}
.plan-card-note .el-icon {
  color: #e6a23c;
  font-size: 14px;
  flex-shrink: 0;
  margin-top: 2px;
}
.plan-card-note-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  word-break: break-all;
}

.plan-card-actions {
  display: flex;
  justify-content: flex-end;
  gap: 4px;
  padding-top: 10px;
  border-top: 1px dashed #ebeef5;
  margin-top: auto;
}
.plan-card-actions .el-button {
  padding: 4px 8px;
}
.plan-card-actions .el-button + .el-button {
  margin-left: 0;
}
.must-params-alert {
  margin: 0 0 16px;
  padding: 10px 14px;
  background: linear-gradient(135deg, #fff5f5 0%, #fff1f0 100%);
  border: 1px solid #fcdcdc;
  border-radius: 8px;
  border-left: 3px solid #f56c6c;
}
.must-params-alert-header {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: #f56c6c;
  font-weight: 500;
  margin-bottom: 8px;
}
.must-params-alert-header .el-icon { font-size: 16px; flex-shrink: 0; }
.must-params-alert-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.must-params-alert-tags .must-star { color: #f56c6c; font-weight: 700; margin-right: 2px; }
.plan-form-content { max-height: 60vh; overflow-y: auto; padding-right: 8px; margin-right: -8px; }
.plan-form-content::-webkit-scrollbar { width: 6px; }
.plan-form-content::-webkit-scrollbar-track { background: transparent; }
.plan-form-content::-webkit-scrollbar-thumb { background-color: transparent; border-radius: 3px; transition: background-color 0.3s; }
.plan-form-content:hover::-webkit-scrollbar-thumb { background-color: rgba(144, 147, 153, 0.3); }
.args-config-container { width: 100%; }
.args-select-row { margin-bottom: 12px; }
.args-selector { border: 1px solid #e4e7ed; border-radius: 4px; padding: 12px; background: #fafafa; max-height: 300px; overflow-y: auto; }
.spec-item { display: flex; align-items: flex-start; padding: 10px 0; border-bottom: 1px dashed #e4e7ed; gap: 12px; }
.spec-item:last-child { border-bottom: none; }
.spec-label { min-width: 80px; max-width: 160px; flex-shrink: 0; font-weight: 500; color: #606266; padding-top: 4px; display: flex; align-items: center; white-space: nowrap; }
.must-star { color: #f56c6c; font-size: 16px; font-weight: 700; margin-right: 2px; line-height: 1; }
.spec-values { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.spec-values :deep(.el-radio-group) { display: flex; flex-wrap: wrap; gap: 6px; }
.spec-values :deep(.el-radio-button__inner) { padding: 6px 12px; }
.number-input-wrapper { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
.number-range { color: #909399; font-size: 12px; }
.matched-attr-info { margin-top: 2px; }
.system-size-tip { margin-top: 4px; }
.args-actions { margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap; }
.args-preview { padding: 0; }
.preview-header { display: flex; justify-content: space-between; align-items: center; }
.preview-list { max-height: 200px; overflow-y: auto; }
.preview-item { display: flex; padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
.preview-item:last-child { border-bottom: none; }
.preview-label { width: 120px; flex-shrink: 0; color: #606266; font-weight: 500; }
.preview-value { color: #409eff; font-weight: 500; }
.preview-value.not-selected { color: #c0c4cc; font-style: italic; }
.json-preview { background: #f5f7fa; border: 1px solid #e4e7ed; border-radius: 4px; padding: 12px; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 12px; color: #606266; max-height: 200px; overflow: auto; white-space: pre-wrap; word-break: break-all; margin: 0; }
.unit-input-row { display: flex; align-items: center; gap: 6px; width: 100%; }
.unit-text { font-size: 13px; color: #606266; flex-shrink: 0; white-space: nowrap; }
</style>
