<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Refresh, ArrowDown, QuestionFilled } from '@element-plus/icons-vue'
import type { CategoryTree, Country, Good, GoodDetail, Tag } from '@/types'
import { goodsApi } from '@/api/goods'
import { originGoodsApi } from '@/api/origin-goods'
import { productFamiliesApi } from '@/api/product-families'
import { countriesApi } from '@/api/countries'
import { syncApi } from '@/api/sync'
import { cleanLinkName, deriveLinkTagNames, linkDims } from '@/utils/origin-name'
import type { OriginGoodTagsResult } from '@/types'
import { findCategoryPath, buildCascader } from '@/utils/category-tree'

const props = defineProps<{
  visible: boolean
  goodId: string
  countries: Country[]
  categories: CategoryTree[]
  /** 分组后的标签选项 {id,label,tags} */
  tagOptionGroups: Array<{ id: string; label: string; tags: Tag[] }>
}>()

const emit = defineEmits<{
  (e: 'update:visible', v: boolean): void
  /** 商品数据变化（保存/同步/族变更），父级刷新左右树 */
  (e: 'changed'): void
  (e: 'deleted'): void
  /** 快捷新建国家后由父级刷新字典 */
  (e: 'dictionaries-changed'): void
}>()

const editLoading = ref(false)
const editDetailLoading = ref(false)
/** 正在同步详情的成员行 id */
const detailSyncing = ref<string>('')
const editGood = ref<Good | GoodDetail | null>(null)
const editForm = ref({
  id: '', goodName: '', goodImage: '', countryId: '', cascaderCategory: [] as string[],
  categoryId: '', positionId: '',
})

const editIsCustom = computed(() => editGood.value?.originGood?.isCustom === true)
const categoryCascader = computed(() => buildCascader(props.categories))

// ─── 自定义商品内容编辑 ───
const customContentForm = ref({
  goodPrice: '', productCode: '', englishName: '', productionCycleHours: undefined as number | undefined,
  minWeightG: '', productionProcess: '', materialDescription: '',
  blankDesignUrl: '', detailsPageVideoUrl: '', textureName: '', reminder: '',
  productPerformance: '', applicableScenarios: '', washingInstructions: '', specialDescription: '',
  designExplanation: '', designArea: '', pictureRequest: '',
  sizeChartJson: '{\n  "columns": [],\n  "rows": []\n}',
  packageSpecsJson: '{\n  "rows": []\n}',
  optionsJson: '{}',
  mediaJson: '{}',
  variants: [] as Array<{
    sku: string; sizeId: string; sizeName: string; colorId: string; colorName: string; colorHex: string; imageUrl: string;
    price: string; originalPrice: string; weightG: string; boxLengthCm: string;
    boxWidthCm: string; boxHeightCm: string; designDataJson: string; enabled: boolean
  }>,
})

function fillCustomContent(g: GoodDetail) {
  const detail = g.originDetail ?? {}
  customContentForm.value = {
    goodPrice: g.originGood?.goodPrice ?? '',
    productCode: String(detail.productCode ?? ''),
    englishName: String(detail.englishName ?? ''),
    productionCycleHours: detail.productionCycleHours == null ? undefined : Number(detail.productionCycleHours),
    minWeightG: String(detail.minWeightG ?? ''),
    productionProcess: String(detail.productionProcess ?? ''),
    materialDescription: String(detail.materialDescription ?? ''),
    blankDesignUrl: String(detail.blankDesignUrl ?? ''),
    detailsPageVideoUrl: String(detail.detailsPageVideoUrl ?? ''),
    textureName: String(detail.textureName ?? ''),
    reminder: String(detail.reminder ?? ''),
    productPerformance: String(detail.productPerformance ?? ''),
    applicableScenarios: String(detail.applicableScenarios ?? ''),
    washingInstructions: String(detail.washingInstructions ?? ''),
    specialDescription: String(detail.specialDescription ?? ''),
    designExplanation: String(detail.designExplanation ?? ''),
    designArea: String(detail.designArea ?? ''),
    pictureRequest: String(detail.pictureRequest ?? ''),
    sizeChartJson: JSON.stringify(detail.sizeChart ?? { columns: [], rows: [] }, null, 2),
    packageSpecsJson: JSON.stringify(detail.packageSpecs ?? { rows: [] }, null, 2),
    optionsJson: JSON.stringify(detail.options ?? {}, null, 2),
    mediaJson: JSON.stringify(detail.media ?? {}, null, 2),
    variants: g.variants.map((variant) => ({
      sku: variant.sku,
      sizeId: String(variant.sizeId ?? ''),
      sizeName: String(variant.sizeName ?? ''),
      colorId: String(variant.colorId ?? ''),
      colorName: String(variant.colorName ?? ''),
      colorHex: String(variant.colorHex ?? ''),
      imageUrl: String(variant.imageUrl ?? ''),
      price: String(variant.price ?? ''),
      originalPrice: String(variant.originalPrice ?? ''),
      weightG: String(variant.weightG ?? ''),
      boxLengthCm: String(variant.boxLengthCm ?? ''),
      boxWidthCm: String(variant.boxWidthCm ?? ''),
      boxHeightCm: String(variant.boxHeightCm ?? ''),
      designDataJson: JSON.stringify(variant.designData ?? {}, null, 2),
      enabled: variant.enabled,
    })),
  }
}

function addCustomVariant() {
  customContentForm.value.variants.push({
    sku: '', sizeId: '', sizeName: '', colorId: '', colorName: '', colorHex: '', imageUrl: '', price: '',
    originalPrice: '', weightG: '', boxLengthCm: '', boxWidthCm: '', boxHeightCm: '', designDataJson: '{}', enabled: true,
  })
}

// ─── 关联原产品（族成员，可展开查看详情 / 配标签 / 改价格） ───
interface FamilyMemberRow {
  id: string
  goodName: string
  goodImage: string | null
  source: string
  delisted: boolean
  sdsGoodId: string
  goodPrice: string | null
  variantCount: number
  logisticsLabel: string | null
  craftLabel: string | null
  warehouseLabel: string | null
  tagsManual: boolean
  tags: Array<{ id: string; tagName: string; tagColor: string | null; manual: boolean }>
}
interface MemberPriceCell {
  sizeId: string
  colorId: string
  sizeName: string | null
  colorName: string | null
  printCount: string
  craft: string
  logistics: string
  price: string
  manual: boolean
  editPrice: string
}
const editFamilyId = ref<string>('')
const editFamilyCode = ref<string | null>(null)
const editFamilyMembers = ref<FamilyMemberRow[]>([])
const familyMemberCount = ref<number | null>(null)
const editFamilyLoaded = ref(false)
const editFamilyAddKw = ref('')
const editFamilyCandidates = ref<Array<{ id: string; goodName: string }>>([])
const expandedMemberKeys = ref<Set<string>>(new Set())
const memberTagEdits = ref<Record<string, string[]>>({})
const memberTagSaving = ref<string>('')
/** 成员展开面板的 尺码×颜色→价格 编辑态（key = og id） */
const memberPrices = ref<Record<string, MemberPriceCell[]>>({})
const memberPriceSaving = ref<string>('')
/** 族原始详情（含 priceMatrix） */
const familyDetailRaw = ref<any>(null)

// ─── 族级并集视图：尺码表/包装规格（族物化并集）+ SKU（价格矩阵 = 各链接动态合并） ───
// 尺码行把 measurements 数组摊平成 { 列key: cm }（与成员级渲染一致，否则格子全空）
const familySizeRows = computed(() =>
  ((familyDetailRaw.value?.sizeChart?.rows ?? []) as any[]).map((row) => ({
    ...row,
    ...(row.measurements ?? []).reduce((out: Record<string, string>, item: any) => {
      out[item.key] = item.cm ?? '-'
      return out
    }, {}),
  })),
)
const familySizeColumns = computed(() => familyDetailRaw.value?.sizeChart?.columns ?? [])
const familyPackageRows = computed(() => familyDetailRaw.value?.packageSpecs?.rows ?? [])
const familySkuRows = computed(() => {
  const rows = (familyDetailRaw.value?.priceMatrix?.rows ?? []) as any[]
  return rows.map((r) => ({ ...r, sourceCount: Array.isArray(r.sources) ? r.sources.length : 0 }))
})

function toggleMemberExpand(key: string) {
  const next = new Set(expandedMemberKeys.value)
  if (next.has(key)) next.delete(key)
  else {
    next.add(key)
    loadMemberPrices(key)
    ensureMemberDetail(key)
  }
  expandedMemberKeys.value = next
}

// ─── 成员详情（懒加载：GET /origin-goods/:id） ───
const memberDetailCache = ref<Record<string, any>>({})

function memberDetail(id: string) {
  return memberDetailCache.value[id]
}

async function ensureMemberDetail(ogId: string) {
  const row = editFamilyMembers.value.find((m) => m.id === ogId)
  if (!row || row.source !== 'SDS' || memberDetailCache.value[ogId] !== undefined) return
  memberDetailCache.value[ogId] = { loading: true }
  try {
    memberDetailCache.value[ogId] = await originGoodsApi.getOriginGood(ogId)
  } catch {
    memberDetailCache.value[ogId] = null
  }
}

function memberSizeColumns(id: string) {
  return memberDetail(id)?.detail?.sizeChart?.columns ?? []
}

function memberSizeRows(id: string) {
  const rows = memberDetail(id)?.detail?.sizeChart?.rows ?? []
  return rows.map((row: any) => ({
    ...row,
    ...(row.measurements ?? []).reduce((out: Record<string, string>, item: any) => {
      out[item.key] = item.cm ?? '-'
      return out
    }, {}),
  }))
}

function memberPackageRows(id: string) {
  return memberDetail(id)?.detail?.packageSpecs?.rows ?? []
}

function memberVariants(id: string) {
  return memberDetail(id)?.variants ?? []
}

// 矩阵三个归因维度的合法取值（与 api 端 auto-tag-rules 一致）
const PRINT_COUNT_VALUES = ['单面印花', '双面印花']
const CRAFT_VALUES = ['烫画', '直喷', '不打印']
const LOGISTICS_VALUES = ['包邮', '不包邮']

/** 成员的矩阵归因维度：优先取有效标签（人工接管后仍准确），回退按名称解析 */
function memberDims(id: string) {
  const row = editFamilyMembers.value.find((m) => m.id === id)
  const fallback = linkDims(row?.goodName ?? null)
  const tagNames = ((row as any)?.originGoodTags ?? [])
    .map((r: any) => r.tag?.tagName)
    .filter(Boolean) as string[]
  const crafts = CRAFT_VALUES.filter((v) => tagNames.includes(v))
  return {
    logistics: LOGISTICS_VALUES.find((v) => tagNames.includes(v)) ?? fallback.logistics,
    crafts: crafts.length ? crafts : fallback.crafts,
    printCount: PRINT_COUNT_VALUES.find((v) => tagNames.includes(v)) ?? fallback.printCount,
  }
}

async function handleSyncMemberDetail(row: { id: string; sdsGoodId: string }) {
  if (!row.sdsGoodId || detailSyncing.value) return
  detailSyncing.value = row.id
  try {
    const result = await syncApi.syncOneProductDetail(row.sdsGoodId)
    memberDetailCache.value[row.id] = await originGoodsApi.getOriginGood(row.id)
    ElMessage.success(`详情同步完成，共 ${result.variants} 个 SKU`)
    emit('changed')
  } catch (error: any) {
    ElMessage.error(error?.response?.data?.message || '商品详情同步失败')
  } finally { detailSyncing.value = '' }
}

/** 该链接在族价格矩阵中的格子（印花数量/工艺/物流 三维即该链接的归因标签） */
function loadMemberPrices(ogId: string) {
  const dims = memberDims(ogId)
  if (!dims.printCount || !dims.crafts.length || !dims.logistics) {
    memberPrices.value[ogId] = []
    return
  }
  const matrixRows = (familyDetailRaw.value?.priceMatrix?.rows ?? []) as any[]
  memberPrices.value[ogId] = matrixRows
    .filter((r: any) =>
      r.printCount === dims.printCount &&
      dims.crafts.includes(r.craft) &&
      r.logistics === dims.logistics)
    .map((r: any) => ({
      sizeId: String(r.sizeId),
      colorId: String(r.colorId),
      sizeName: r.sizeName ?? null,
      colorName: r.colorName ?? null,
      printCount: r.printCount,
      craft: r.craft,
      logistics: r.logistics,
      price: String(r.price ?? ''),
      manual: Boolean(r.manual),
      editPrice: String(r.price ?? ''),
    }))
}

async function saveMemberPrices(row: { id: string }) {
  const cells = (memberPrices.value[row.id] ?? []).filter((c) => c.editPrice !== '' && c.editPrice !== c.price)
  if (!cells.length) { ElMessage.info('价格没有修改'); return }
  memberPriceSaving.value = row.id
  try {
    await productFamiliesApi.putOverrides(editFamilyId.value, cells.map((c) => ({
      sizeId: c.sizeId, colorId: c.colorId, printCount: c.printCount, craft: c.craft, logistics: c.logistics, price: c.editPrice,
    })))
    ElMessage.success('价格已保存')
    await loadEditFamily()
    emit('changed')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || '保存失败')
  } finally { memberPriceSaving.value = '' }
}

function initEditFamily(family: { familyId?: string; familyCode?: string | null } | null) {
  editFamilyId.value = family?.familyId ?? ''
  editFamilyCode.value = family?.familyCode ?? null
  editFamilyMembers.value = []
  familyMemberCount.value = null
  editFamilyLoaded.value = false
  editFamilyAddKw.value = ''
  editFamilyCandidates.value = []
  expandedMemberKeys.value = new Set()
  memberTagEdits.value = {}
  memberPrices.value = {}
  familyDetailRaw.value = null
  if (editFamilyId.value) loadEditFamily()
}

async function loadEditFamily() {
  if (!editFamilyId.value) return
  try {
    const f = await productFamiliesApi.detail(editFamilyId.value) as any
    familyDetailRaw.value = f
    editFamilyCode.value = f.familyCode
    familyMemberCount.value = f._count.originGoods
    const primaryOgId = String(editGood.value?.originGoodId ?? '')
    const members: FamilyMemberRow[] = (f.originGoods ?? []).map((m: any) => ({
      id: String(m.id),
      goodName: m.goodName ?? '',
      goodImage: m.goodImage ?? null,
      source: m.source,
      delisted: Boolean(m.delisted),
      sdsGoodId: m.sdsGoodId ?? '',
      goodPrice: m.goodPrice ?? null,
      variantCount: m._count?.variants ?? 0,
      logisticsLabel: m.logisticsLabel ?? null,
      craftLabel: m.craftLabel ?? null,
      warehouseLabel: m.warehouseLabel ?? null,
      tagsManual: Boolean(m.tagsManual),
      tags: (m.originGoodTags ?? []).map((t: any) => ({
        id: String(t.tag.id),
        tagName: t.tag.tagName,
        tagColor: t.tag.tagColor ?? null,
        manual: Boolean(t.manual),
      })),
    }))
    // 当前商品的链接排最前，其余按 id 稳定排序（界面上不区分主次）
    members.sort((a, b) => {
      const aP = a.id === primaryOgId ? 0 : 1
      const bP = b.id === primaryOgId ? 0 : 1
      return aP - bP || a.id.localeCompare(b.id)
    })
    editFamilyMembers.value = members
    const edits: Record<string, string[]> = {}
    for (const m of members) edits[m.id] = m.tags.map((t) => t.id)
    memberTagEdits.value = edits
    for (const key of expandedMemberKeys.value) loadMemberPrices(key)
  } finally {
    editFamilyLoaded.value = true
  }
}

async function saveMemberTags(row: { id: string }) {
  memberTagSaving.value = row.id
  try {
    const res = await originGoodsApi.updateTags(row.id, memberTagEdits.value[row.id] ?? [])
    const filled = (res as OriginGoodTagsResult | undefined)?.filledDimensionTags ?? []
    ElMessage.success(filled.length ? `标签已保存（缺维度已自动补齐：${filled.join('、')}）` : '标签已保存')
    await loadEditFamily()
    emit('changed')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || '保存失败')
  } finally { memberTagSaving.value = '' }
}

async function resetMemberTags(row: { id: string }) {
  memberTagSaving.value = row.id
  try {
    await originGoodsApi.resetTags(row.id)
    ElMessage.success('已恢复按链接名称自动解析')
    await loadEditFamily()
    emit('changed')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || '恢复失败')
  } finally { memberTagSaving.value = '' }
}

async function searchFamilyCandidates(q: string) {
  if (!q) { editFamilyCandidates.value = []; return }
  try {
    const res = await originGoodsApi.getOriginGoodsList({ keyword: q, page: 1, pageSize: 20 })
    editFamilyCandidates.value = res.items.map((o: any) => ({ id: String(o.id), goodName: o.goodName ?? o.sdsGoodId }))
  } catch { editFamilyCandidates.value = [] }
}

async function addFamilyMember(c: { id: string; goodName: string }) {
  if (!editFamilyId.value) { ElMessage.warning('该商品尚未成族'); return }
  try {
    await productFamiliesApi.updateMembers(editFamilyId.value, { addOriginGoodIds: [c.id] })
    ElMessage.success('已加入族')
    editFamilyAddKw.value = ''
    await loadEditFamily()
    emit('changed')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || '加入失败')
  }
}

async function removeFamilyMember(m: { id: string }) {
  if (!editFamilyId.value) return
  try {
    await ElMessageBox.confirm('确定把该链接移出族吗？', '确认', {
      type: 'warning', confirmButtonText: '移除', cancelButtonText: '取消',
    })
  } catch { return }
  try {
    await productFamiliesApi.updateMembers(editFamilyId.value, { removeOriginGoodIds: [m.id] })
    ElMessage.success('已移除出族')
    await loadEditFamily()
    emit('changed')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || '移除失败')
  }
}

// ─── 打开 / 保存 / 删除 / 同步 ───
watch(() => props.visible, (v) => {
  if (v && props.goodId) initLoad()
})

async function initLoad() {
  editGood.value = null
  editDetailLoading.value = true
  try {
    const detail = await goodsApi.getGoodById(props.goodId)
    applyGood(detail)
  } catch {
    ElMessage.warning('商品详情加载失败')
  } finally {
    editDetailLoading.value = false
  }
}

function applyGood(g: GoodDetail) {
  editGood.value = g
  // 名称展示解析为「品名 型号」，保存后即采用解析名
  editForm.value = {
    id: g.id,
    goodName: cleanLinkName(g.goodName) || g.goodName,
    goodImage: g.goodImage || g.originGood?.goodImage || '',
    countryId: g.countryId,
    cascaderCategory: findCategoryPath(props.categories, g.categoryId),
    categoryId: g.categoryId,
    positionId: g.positionId || '',
  }
  initEditFamily((g as any).originGood?.family ?? null)
  if (g.originGood?.isCustom) fillCustomContent(g)
}

async function quickCreateCountry() {
  try {
    const { value } = await ElMessageBox.prompt('请输入国家名称', '新增国家', {
      confirmButtonText: '新增', cancelButtonText: '取消', inputPlaceholder: '国家名称',
    })
    if (!value.trim()) return
    const created = await countriesApi.createCountry({ countryName: value.trim() } as any)
    emit('dictionaries-changed')
    if (created?.id) editForm.value.countryId = created.id
    ElMessage.success('已创建并选中')
  } catch {}
}

function onEditCascaderChange(val: any) {
  const path = Array.isArray(val) ? val : []
  editForm.value.categoryId = path.length ? String(path[path.length - 1]) : ''
}

async function handleEditSubmit() {
  let customPayload: any = null
  if (editIsCustom.value) {
    let sizeChart: Record<string, unknown>
    let packageSpecs: Record<string, unknown>
    let options: Record<string, unknown>
    let media: Record<string, unknown>
    try {
      sizeChart = JSON.parse(customContentForm.value.sizeChartJson)
      packageSpecs = JSON.parse(customContentForm.value.packageSpecsJson)
      options = JSON.parse(customContentForm.value.optionsJson)
      media = JSON.parse(customContentForm.value.mediaJson)
      for (const variant of customContentForm.value.variants) JSON.parse(variant.designDataJson)
    } catch {
      ElMessage.error('尺码表、包装规格、选项、媒体或 SKU 设计数据不是有效 JSON')
      return
    }
    if (customContentForm.value.variants.some((variant) => !variant.sku.trim())) {
      ElMessage.error('SKU 不能为空')
      return
    }
    customPayload = {
      goodName: editForm.value.goodName,
      goodImage: editForm.value.goodImage || null,
      goodPrice: customContentForm.value.goodPrice || null,
      detail: {
        productCode: customContentForm.value.productCode || null,
        englishName: customContentForm.value.englishName || null,
        productionCycleHours: customContentForm.value.productionCycleHours ?? null,
        minWeightG: customContentForm.value.minWeightG || null,
        productionProcess: customContentForm.value.productionProcess || null,
        materialDescription: customContentForm.value.materialDescription || null,
        blankDesignUrl: customContentForm.value.blankDesignUrl || null,
        detailsPageVideoUrl: customContentForm.value.detailsPageVideoUrl || null,
        textureName: customContentForm.value.textureName || null,
        reminder: customContentForm.value.reminder || null,
        productPerformance: customContentForm.value.productPerformance || null,
        applicableScenarios: customContentForm.value.applicableScenarios || null,
        washingInstructions: customContentForm.value.washingInstructions || null,
        specialDescription: customContentForm.value.specialDescription || null,
        designExplanation: customContentForm.value.designExplanation || null,
        designArea: customContentForm.value.designArea || null,
        pictureRequest: customContentForm.value.pictureRequest || null,
        sizeChart,
        packageSpecs,
        options,
        media,
      },
      variants: customContentForm.value.variants.map((variant: any, index: number) => ({
        sku: variant.sku.trim(),
        sizeId: variant.sizeId || null,
        sizeName: variant.sizeName || null,
        colorId: variant.colorId || null,
        colorName: variant.colorName || null,
        colorHex: variant.colorHex || null,
        imageUrl: variant.imageUrl || null,
        price: variant.price || null,
        originalPrice: variant.originalPrice || null,
        weightG: variant.weightG || null,
        boxLengthCm: variant.boxLengthCm || null,
        boxWidthCm: variant.boxWidthCm || null,
        boxHeightCm: variant.boxHeightCm || null,
        designData: JSON.parse(variant.designDataJson),
        enabled: variant.enabled,
        sortOrder: index,
      })),
    }
  }
  editLoading.value = true
  try {
    await goodsApi.updateGood(editForm.value.id, {
      goodName: editForm.value.goodName,
      goodImage: editForm.value.goodImage || null,
      countryId: Number(editForm.value.countryId),
      categoryId: Number(editForm.value.categoryId),
      positionId: editForm.value.positionId ? Number(editForm.value.positionId) : null,
    } as any)
    if (customPayload) await goodsApi.updateCustomGoodContent(editForm.value.id, customPayload)
    ElMessage.success('更新成功')
    emit('update:visible', false)
    emit('changed')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.message || '更新失败')
  } finally { editLoading.value = false }
}

async function handleDeleteGood() {
  const g = editGood.value
  if (!g) return
  try {
    await ElMessageBox.confirm(`确定删除「${g.goodName}」吗？`, '确认', {
      type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消',
    })
  } catch { return }
  await goodsApi.deleteGood(g.id)
  ElMessage.success('删除成功')
  emit('update:visible', false)
  emit('deleted')
}
</script>

<template>
  <el-dialog :model-value="visible" :title="cleanLinkName(editGood?.goodName) || '编辑商品'" width="900px" destroy-on-close @update:model-value="emit('update:visible', $event)">
    <div v-if="!editIsCustom" class="edit-merged-box">
      <div class="edit-merged-title">
        关联原产品
        <el-tooltip content="同族链接合并为一个商品：尺码/包装并集 + 价格矩阵。展开成员可查看详情、修正标签、调整价格。">
          <el-icon><QuestionFilled /></el-icon>
        </el-tooltip>
        <span v-if="editFamilyCode" class="edit-family-code">族 {{ editFamilyCode }} · {{ familyMemberCount ?? editFamilyMembers.length }} 条链接</span>
      </div>
      <div class="edit-merged-list">
        <template v-for="row in editFamilyMembers" :key="row.id">
          <div class="edit-merged-item">
            <el-icon
              class="member-expand-toggle"
              :class="{ 'is-expanded': expandedMemberKeys.has(row.id) }"
              @click="toggleMemberExpand(row.id)"
            ><ArrowDown /></el-icon>
            <span class="edit-merged-name" :title="row.goodName">{{ row.goodName }}</span>
            <span v-if="row.tags.length || deriveLinkTagNames(row.goodName).length" class="member-chips">
              <template v-if="row.tags.length">
                <span v-for="c in row.tags" :key="c.id" class="member-chip" :class="{ 'is-manual': c.manual }">{{ c.tagName }}</span>
              </template>
              <template v-else>
                <span v-for="c in deriveLinkTagNames(row.goodName)" :key="c" class="member-chip is-preview">{{ c }}</span>
              </template>
            </span>
            <span v-if="row.tagsManual" class="member-manual-flag" title="人工接管：自动同步不再覆盖该链接的标签">人工</span>
            <div class="edit-merged-actions">
              <el-button size="small" link type="danger" @click="removeFamilyMember(row)">移除出族</el-button>
            </div>
          </div>
          <div v-if="expandedMemberKeys.has(row.id)" class="member-detail-panel">
            <div class="member-detail-grid">
              <span class="md-label">SDS ID</span><span class="md-val">{{ row.sdsGoodId || '-' }}</span>
              <span class="md-label">链接价格</span><span class="md-val">{{ row.goodPrice ? `¥${row.goodPrice}` : '-' }}</span>
              <span class="md-label">SKU 数</span><span class="md-val">{{ row.id === editGood?.originGoodId ? (editGood?.originGood?.variantCount ?? row.variantCount) : row.variantCount }}</span>
              <span class="md-label">物流备注</span><span class="md-val">{{ row.logisticsLabel || '-' }}</span>
              <span class="md-label">工艺位置</span><span class="md-val">{{ row.craftLabel || '-' }}</span>
              <span class="md-label">仓库</span><span class="md-val">{{ row.warehouseLabel || '-' }}</span>
            </div>
              <div class="member-tags-edit">
                <span class="md-label">标签</span>
                <el-select
                  v-model="memberTagEdits[row.id]"
                  multiple filterable size="small"
                  placeholder="选择标签"
                  style="flex:1"
                >
                  <el-option-group v-for="g in tagOptionGroups" :key="g.id" :label="g.label">
                    <el-option v-for="t in g.tags" :key="t.id" :label="t.tagName" :value="t.id" />
                  </el-option-group>
                </el-select>
                <el-button size="small" type="primary" :loading="memberTagSaving === row.id" @click="saveMemberTags(row)">保存标签</el-button>
                <el-button v-if="row.tagsManual" size="small" :loading="memberTagSaving === row.id" @click="resetMemberTags(row)">恢复自动</el-button>
              </div>
            <div v-if="row.craftLabel && row.logisticsLabel" class="member-price-block">
              <div class="member-price-head">
                <el-button size="small" type="primary" :loading="memberPriceSaving === row.id" @click="saveMemberPrices(row)">保存价格</el-button>
              </div>
              <el-table :data="memberPrices[row.id] ?? []" border size="small" max-height="240">
                <el-table-column prop="sizeName" label="尺码" width="100" />
                <el-table-column prop="colorName" label="颜色" min-width="120" />
                <el-table-column label="价格" width="130">
                  <template #default="{ row: cell }">
                    <el-input v-model="cell.editPrice" size="small" @input="cell.editPrice = String(cell.editPrice)" />
                  </template>
                </el-table-column>
              </el-table>
            </div>
            <div v-if="row.source === 'SDS'" class="member-extra">
              <div class="member-detail-head">
                <span v-if="!memberDetail(row.id) || memberDetail(row.id).loading" class="md-empty">详情加载中…</span>
                <span v-else-if="!memberDetail(row.id).hasDetail" class="md-empty">尚未同步详情</span>
                <el-button
                  size="small"
                  :icon="Refresh"
                  :loading="detailSyncing === row.id"
                  @click="handleSyncMemberDetail(row)"
                >{{ memberDetail(row.id)?.hasDetail ? '重新同步详情' : '同步详情' }}</el-button>
              </div>
              <el-tabs v-if="memberDetail(row.id) && memberDetail(row.id).hasDetail" class="member-detail-tabs">
                <el-tab-pane label="商品详情">
                  <el-descriptions :column="2" border size="small">
                    <el-descriptions-item label="商品编码">{{ memberDetail(row.id).detail.productCode || '-' }}</el-descriptions-item>
                    <el-descriptions-item label="英文名称">{{ memberDetail(row.id).detail.englishName || '-' }}</el-descriptions-item>
                    <el-descriptions-item label="生产周期">{{ memberDetail(row.id).detail.productionCycleHours ?? '-' }} 小时</el-descriptions-item>
                    <el-descriptions-item label="净重">{{ memberDetail(row.id).detail.minWeightG ?? '-' }} g</el-descriptions-item>
                    <el-descriptions-item label="生产工艺">{{ memberDetail(row.id).detail.productionProcess || '-' }}</el-descriptions-item>
                    <el-descriptions-item label="材质">{{ memberDetail(row.id).detail.materialDescription || '-' }}</el-descriptions-item>
                  </el-descriptions>
                </el-tab-pane>
                <el-tab-pane :label="`尺码表 (${memberSizeRows(row.id).length})`">
                  <el-table :data="memberSizeRows(row.id)" border size="small" max-height="240">
                    <el-table-column prop="sizeName" label="尺码" width="100" fixed />
                    <el-table-column
                      v-for="column in memberSizeColumns(row.id)"
                      :key="column.key"
                      :prop="column.key"
                      :label="`${column.name} (cm)`"
                      min-width="110"
                    />
                  </el-table>
                </el-tab-pane>
                <el-tab-pane :label="`包装规格 (${memberPackageRows(row.id).length})`">
                  <el-table :data="memberPackageRows(row.id)" border size="small" max-height="240">
                    <el-table-column prop="sizeName" label="尺码" width="90" fixed />
                    <el-table-column label="包装尺寸 (cm)" min-width="150">
                      <template #default="{ row: pkg }">
                        {{ pkg.dimensionsCm ? `${pkg.dimensionsCm.length}×${pkg.dimensionsCm.width}×${pkg.dimensionsCm.height}` : '-' }}
                      </template>
                    </el-table-column>
                    <el-table-column prop="volumeCm3" label="体积 (cm³)" width="110" />
                    <el-table-column prop="grossWeightG" label="含包装重量 (g)" width="140" />
                  </el-table>
                </el-tab-pane>
                <el-tab-pane :label="`SKU (${memberVariants(row.id).length})`">
                  <el-table :data="memberVariants(row.id)" border size="small" max-height="240">
                    <el-table-column prop="sku" label="SKU" min-width="160" fixed />
                    <el-table-column label="物流" width="100">
                      <template #default>{{ memberDims(row.id).logistics || '-' }}</template>
                    </el-table-column>
                    <el-table-column label="工艺" width="90">
                      <template #default>{{ memberDims(row.id).crafts.join(' / ') || '-' }}</template>
                    </el-table-column>
                    <el-table-column label="印花数量" width="95">
                      <template #default>{{ memberDims(row.id).printCount || '-' }}</template>
                    </el-table-column>
                    <el-table-column prop="sizeName" label="尺码" width="85" />
                    <el-table-column prop="colorName" label="颜色" width="100" />
                    <el-table-column prop="price" label="价格" width="90" />
                    <el-table-column label="状态" width="80">
                      <template #default="{ row: variant }">
                        <el-tag :type="variant.enabled ? 'success' : 'info'" size="small">{{ variant.enabled ? '可用' : '停用' }}</el-tag>
                      </template>
                    </el-table-column>
                  </el-table>
                </el-tab-pane>
              </el-tabs>
            </div>
          </div>
        </template>
        <div v-if="!editFamilyLoaded && editFamilyId" class="edit-family-loading">族成员加载中…</div>
        <div v-else-if="!editFamilyId" class="edit-family-loading">暂未成族：配置合并时自动成族</div>
      </div>
      <el-select v-model="editFamilyAddKw" filterable remote :remote-method="searchFamilyCandidates"
        placeholder="搜索原产品名称添加到族" clearable style="width:100%">
        <el-option v-for="c in editFamilyCandidates" :key="c.id" :label="c.goodName" :value="String(c.id)"
          @click="addFamilyMember(c)" />
      </el-select>
    </div>
    <el-form v-loading="editDetailLoading" label-width="80px" style="margin-top: 16px">
      <el-form-item label="名称"><el-input v-model="editForm.goodName" /></el-form-item>
      <el-form-item label="图片">
        <ImageUpload v-model="editForm.goodImage" label="上传图片" />
      </el-form-item>
      <el-form-item label="国家">
        <div class="select-inline">
          <el-select v-model="editForm.countryId" filterable placeholder="请选择国家">
            <el-option v-for="c in countries" :key="c.id" :label="c.countryName" :value="c.id" />
          </el-select>
          <el-button text :icon="Plus" @click="quickCreateCountry" />
        </div>
      </el-form-item>
      <el-form-item label="分类">
        <el-cascader v-model="editForm.cascaderCategory" :options="categoryCascader as any" :props="{ checkStrictly: true }" placeholder="请选择分类" @change="onEditCascaderChange" />
      </el-form-item>
      <el-form-item v-if="editIsCustom" label="基础价格">
        <el-input v-model="customContentForm.goodPrice" placeholder="例如 28.00" />
      </el-form-item>
    </el-form>

    <!-- 自定义商品内容编辑（SDS 商品的详情在「关联原产品」成员面板内查看） -->
    <el-tabs v-if="editIsCustom" class="detail-tabs">
      <el-tab-pane label="商品详情">
        <el-form label-width="100px" class="custom-detail-form">
          <el-form-item label="商品编码"><el-input v-model="customContentForm.productCode" /></el-form-item>
          <el-form-item label="英文名称"><el-input v-model="customContentForm.englishName" /></el-form-item>
          <el-form-item label="生产周期"><el-input-number v-model="customContentForm.productionCycleHours" :min="0" /><span style="margin-left:8px">小时</span></el-form-item>
          <el-form-item label="净重"><el-input v-model="customContentForm.minWeightG"><template #append>g</template></el-input></el-form-item>
          <el-form-item label="生产工艺"><el-input v-model="customContentForm.productionProcess" type="textarea" /></el-form-item>
          <el-form-item label="材质"><el-input v-model="customContentForm.materialDescription" type="textarea" /></el-form-item>
          <el-form-item label="空白设计图"><el-input v-model="customContentForm.blankDesignUrl" /></el-form-item>
          <el-form-item label="详情视频"><el-input v-model="customContentForm.detailsPageVideoUrl" /></el-form-item>
          <el-form-item label="面料名称"><el-input v-model="customContentForm.textureName" /></el-form-item>
          <el-form-item label="温馨提示"><el-input v-model="customContentForm.reminder" type="textarea" /></el-form-item>
          <el-form-item label="产品性能"><el-input v-model="customContentForm.productPerformance" type="textarea" /></el-form-item>
          <el-form-item label="适用场景"><el-input v-model="customContentForm.applicableScenarios" type="textarea" /></el-form-item>
          <el-form-item label="洗涤说明"><el-input v-model="customContentForm.washingInstructions" type="textarea" /></el-form-item>
          <el-form-item label="特殊说明"><el-input v-model="customContentForm.specialDescription" type="textarea" /></el-form-item>
          <el-form-item label="设计说明"><el-input v-model="customContentForm.designExplanation" type="textarea" /></el-form-item>
          <el-form-item label="设计区域"><el-input v-model="customContentForm.designArea" /></el-form-item>
          <el-form-item label="图片要求"><el-input v-model="customContentForm.pictureRequest" type="textarea" /></el-form-item>
          <el-form-item label="商品选项 JSON"><el-input v-model="customContentForm.optionsJson" type="textarea" :rows="6" /></el-form-item>
          <el-form-item label="媒体数据 JSON"><el-input v-model="customContentForm.mediaJson" type="textarea" :rows="6" /></el-form-item>
        </el-form>
      </el-tab-pane>
      <el-tab-pane label="尺码表">
        <el-input v-model="customContentForm.sizeChartJson" type="textarea" :rows="14" placeholder="尺码表 JSON" />
      </el-tab-pane>
      <el-tab-pane label="包装规格">
        <el-input v-model="customContentForm.packageSpecsJson" type="textarea" :rows="14" placeholder="包装规格 JSON" />
      </el-tab-pane>
      <el-tab-pane :label="`SKU (${customContentForm.variants.length})`">
        <div style="display:flex;justify-content:flex-end;margin-bottom:8px"><el-button size="small" :icon="Plus" @click="addCustomVariant">添加 SKU</el-button></div>
        <el-table :data="customContentForm.variants" border max-height="320">
          <el-table-column label="SKU" min-width="160"><template #default="{ row }"><el-input v-model="row.sku" /></template></el-table-column>
          <el-table-column label="尺码 ID" width="120"><template #default="{ row }"><el-input v-model="row.sizeId" /></template></el-table-column>
          <el-table-column label="尺码" width="120"><template #default="{ row }"><el-input v-model="row.sizeName" /></template></el-table-column>
          <el-table-column label="颜色 ID" width="120"><template #default="{ row }"><el-input v-model="row.colorId" /></template></el-table-column>
          <el-table-column label="颜色" width="120"><template #default="{ row }"><el-input v-model="row.colorName" /></template></el-table-column>
          <el-table-column label="色值" width="120"><template #default="{ row }"><el-input v-model="row.colorHex" placeholder="#FFFFFF" /></template></el-table-column>
          <el-table-column label="图片" min-width="180"><template #default="{ row }"><el-input v-model="row.imageUrl" /></template></el-table-column>
          <el-table-column label="价格" width="120"><template #default="{ row }"><el-input v-model="row.price" /></template></el-table-column>
          <el-table-column label="原价" width="120"><template #default="{ row }"><el-input v-model="row.originalPrice" /></template></el-table-column>
          <el-table-column label="重量(g)" width="120"><template #default="{ row }"><el-input v-model="row.weightG" /></template></el-table-column>
          <el-table-column label="包装长(cm)" width="130"><template #default="{ row }"><el-input v-model="row.boxLengthCm" /></template></el-table-column>
          <el-table-column label="包装宽(cm)" width="130"><template #default="{ row }"><el-input v-model="row.boxWidthCm" /></template></el-table-column>
          <el-table-column label="包装高(cm)" width="130"><template #default="{ row }"><el-input v-model="row.boxHeightCm" /></template></el-table-column>
          <el-table-column label="设计数据 JSON" min-width="220"><template #default="{ row }"><el-input v-model="row.designDataJson" type="textarea" :rows="2" /></template></el-table-column>
          <el-table-column label="启用" width="80"><template #default="{ row }"><el-switch v-model="row.enabled" /></template></el-table-column>
          <el-table-column label="操作" width="70"><template #default="{ $index }"><el-button link type="danger" @click="customContentForm.variants.splice($index, 1)">删除</el-button></template></el-table-column>
        </el-table>
      </el-tab-pane>
    </el-tabs>

    <!-- 族级并集视图（弹窗最底部）：尺码表/包装规格为族并集，SKU 为价格矩阵动态合并 -->
    <el-tabs v-if="!editIsCustom && editFamilyId && editFamilyLoaded" class="detail-tabs">
      <el-tab-pane :label="`尺码表 (${familySizeRows.length})`">
        <el-table :data="familySizeRows" border size="small" max-height="260">
          <el-table-column prop="sizeName" label="尺码" width="100" fixed />
          <el-table-column
            v-for="column in familySizeColumns"
            :key="column.key"
            :prop="column.key"
            :label="`${column.name} (cm)`"
            min-width="110"
          />
        </el-table>
      </el-tab-pane>
      <el-tab-pane :label="`包装规格 (${familyPackageRows.length})`">
        <el-table :data="familyPackageRows" border size="small" max-height="260">
          <el-table-column prop="sizeName" label="尺码" width="90" fixed />
          <el-table-column label="包装尺寸 (cm)" min-width="150">
            <template #default="{ row }">
              {{ row.dimensionsCm ? `${row.dimensionsCm.length}×${row.dimensionsCm.width}×${row.dimensionsCm.height}` : '-' }}
            </template>
          </el-table-column>
          <el-table-column prop="volumeCm3" label="体积 (cm³)" width="110" />
          <el-table-column prop="grossWeightG" label="含包装重量 (g)" width="140" />
        </el-table>
      </el-tab-pane>
      <el-tab-pane :label="`SKU (${familySkuRows.length})`">
        <el-table :data="familySkuRows" border size="small" max-height="260">
          <el-table-column label="物流" width="110">
            <template #default="{ row }">{{ row.logistics || '-' }}</template>
          </el-table-column>
          <el-table-column label="工艺" width="100">
            <template #default="{ row }">{{ row.craft || '-' }}</template>
          </el-table-column>
          <el-table-column label="印花数量" width="100">
            <template #default="{ row }">{{ row.printCount || '-' }}</template>
          </el-table-column>
          <el-table-column prop="sizeName" label="尺码" width="90" />
          <el-table-column prop="colorName" label="颜色" min-width="110" />
          <el-table-column label="价格" width="100">
            <template #default="{ row }">{{ row.price }}</template>
          </el-table-column>
          <el-table-column label="链接数" width="80">
            <template #default="{ row }">{{ row.sourceCount || 1 }}</template>
          </el-table-column>
          <el-table-column label="状态" width="90">
            <template #default="{ row }">
              <el-tag :type="row.manual ? 'warning' : 'success'" size="small">{{ row.manual ? '人工价' : '推导价' }}</el-tag>
            </template>
          </el-table-column>
        </el-table>
      </el-tab-pane>
    </el-tabs>
    <template #footer>
      <el-button type="danger" @click="handleDeleteGood">删除</el-button>
      <el-button @click="emit('update:visible', false)">取消</el-button>
      <el-button type="primary" :loading="editLoading" @click="handleEditSubmit">保存</el-button>
    </template>
  </el-dialog>
</template>

<style scoped>
.edit-merged-box { margin-bottom: 4px; }
.edit-merged-title {
  display: flex; align-items: center; gap: 4px;
  font-weight: 600; font-size: 13px; margin-bottom: 8px;
}
.edit-family-code { margin-left: auto; font-weight: 400; font-size: 12px; color: var(--el-color-primary); }
.edit-merged-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.edit-merged-item {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px; background: #f5f7fa; border-radius: 6px; font-size: 13px;
}
.edit-merged-name {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.edit-merged-actions { flex-shrink: 0; display: flex; align-items: center; }
.edit-merged-actions .el-button + .el-button { margin-left: 4px; }
.member-chips { display: flex; align-items: center; gap: 4px; flex-shrink: 0; flex-wrap: nowrap; overflow: hidden; }
.member-chip {
  font-size: 11px; line-height: 16px; padding: 1px 7px; border-radius: 8px;
  background: #ecf5ff; color: var(--el-color-primary); white-space: nowrap;
}
.member-chip.is-manual { background: #f0f9eb; color: var(--el-color-success); }
.member-chip.is-preview { background: #f4f4f5; color: #909399; border: 1px dashed #dcdfe6; }
.member-manual-flag {
  font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 4px;
  background: #f0f9eb; color: var(--el-color-success); flex-shrink: 0;
}
.member-expand-toggle { cursor: pointer; flex-shrink: 0; color: #909399; transition: transform 0.18s; }
.member-expand-toggle.is-expanded { transform: rotate(180deg); }
.member-detail-panel {
  background: #fbfcfe; border: 1px solid #ebeef5; border-radius: 6px;
  padding: 10px 12px; margin: -2px 0 2px 30px;
}
.member-detail-grid {
  display: grid; grid-template-columns: 64px minmax(0, 1fr) 64px minmax(0, 1fr);
  gap: 4px 10px; font-size: 12px; margin-bottom: 8px;
}
.md-label { color: #909399; font-size: 12px; }
.md-val { color: #606266; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member-tags-edit { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.member-tags-edit .md-label { flex-shrink: 0; }
.member-price-block { border-top: 1px dashed #e4e7ed; padding-top: 8px; }
.member-price-head { display: flex; align-items: center; justify-content: flex-end; margin-bottom: 6px; }
.member-extra { border-top: 1px dashed #e4e7ed; padding-top: 8px; }
.md-empty {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: 12px; color: var(--el-text-color-placeholder); padding: 4px 0;
}
.member-detail-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-bottom: 4px;
}
.member-detail-head .md-empty { padding: 0; }
.member-detail-tabs :deep(.el-tabs__header) { margin-bottom: 8px; }
.edit-family-loading { color: var(--el-text-color-placeholder); font-size: 12px; padding: 4px 0; }
.family-detail-tabs { margin-top: 10px; }
.family-detail-tabs :deep(.el-tabs__header) { margin-bottom: 8px; }
.detail-tabs { margin-top: 12px; padding-top: 4px; border-top: 1px solid #ebeef5; }
.select-inline { display: flex; align-items: center; gap: 6px; width: 100%; }
</style>
