import { useCallback, useEffect, useMemo, useState, type Key } from "react"
import { useNavigate } from "react-router-dom"
import {
  CloudDownloadOutlined,
  EditOutlined,
  EyeOutlined,
  LinkOutlined,
  PlusOutlined,
  ReloadOutlined,
} from "@ant-design/icons"
import {
  Alert,
  Button,
  Card,
  Col,
  Descriptions,
  Empty,
  Form,
  Input,
  Modal,
  Popconfirm,
  Row,
  Select,
  Space,
  Switch,
  Table,
  Tag,
  Typography,
  type TableColumnsType,
} from "antd"
import { PageSkeleton, type SkeletonSpec } from "../../utils/skeleton"
import { toast } from "../../utils/toast"
import { listCloudServers, type CloudServer } from "../api/cloud_server"
import {
  createProduct,
  getZjmfFinanceProductConfig,
  listProducts,
  listZjmfFinanceProducts,
  updateProduct,
  type BillingCycle,
  type ConfigField,
  type Product,
  type ProductCreatePayload,
  type ProductPricingItem,
  type ProductType,
  type ZjmfFinanceConfigOption,
  type ZjmfFinanceProductConfig,
  type ZjmfFinanceProductItem,
} from "../api/product"
import { listGroups, type ProductGroup } from "../api/product_catalog"
import { CPU_PRESETS, formatGhz, type CpuPreset } from "../components/cpuPresets"

const { Title, Paragraph, Text } = Typography

const PAGE_SKELETON: SkeletonSpec[] = [
  { type: "header", title: 180, subtitle: 300 },
  { type: "card", h: 120 },
  { type: "table", rows: 8, cols: 6 },
]

function adminErrorDetail(error: unknown, fallback: string) {
  const detail = (error as { response?: { data?: { detail?: unknown } } })?.response?.data?.detail
  const raw = typeof detail === "string" ? detail : detail == null ? "" : JSON.stringify(detail)
  if (!raw) return fallback
  return friendlyUpstreamError(raw)
}

function friendlyUpstreamError(raw: string) {
  const hostMatch = raw.match(/https?:\/\/([^/"\s]+)/i)
  const host = hostMatch?.[1]
  const isConnectRefused = /connectex|connection refused|actively refused/i.test(raw)
  const isLoopback = /127\.0\.0\.1(?::443)?/.test(raw)
  const isDns = /no such host|lookup .* failed/i.test(raw)
  const isTimeout = /timeout|Client\.Timeout exceeded/i.test(raw)

  if (isLoopback && host) {
    return `上游节点连接失败：${host} 当前解析到 127.0.0.1，后端实际在连接本机 443。请检查魔方财务节点域名/DNS，或更换为真实可访问的上游地址。原始错误：${raw}`
  }
  if (isConnectRefused && host) {
    return `上游节点连接被拒绝：${host} 无法建立连接。请检查节点地址、端口、HTTPS 配置和防火墙。原始错误：${raw}`
  }
  if (isDns && host) {
    return `上游节点域名解析失败：${host}。请检查节点域名是否正确、DNS 是否生效。原始错误：${raw}`
  }
  if (isTimeout && host) {
    return `上游节点连接超时：${host}。请检查网络连通性、上游服务状态或稍后重试。原始错误：${raw}`
  }
  return raw
}

type FilterMode = "all" | "unmapped" | "mapped"

interface UpstreamRow extends ZjmfFinanceProductItem {
  mappedProduct: Product | null
}

interface BindState {
  open: boolean
  row: UpstreamRow | null
  productId: number | null
}

interface CreatedResult {
  products: Product[]
  groupId: number
  count: number
}

interface StockSnapshot {
  label: string
  color: string
  stockPolicy: "unlimited" | "tracked"
  stock: number
  qty: number | null
  stockControl: boolean
  hidden: boolean
  retired: boolean
  syncedAt: string
  localUpdated: boolean
  error?: string
}

function stripHtml(value: string | null | undefined) {
  return decodeHtml(value ?? "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim()
}

function decodeHtml(value: string) {
  return value
    .replace(/&lt;/gi, "<")
    .replace(/&gt;/gi, ">")
    .replace(/&amp;/gi, "&")
    .replace(/&quot;/gi, '"')
    .replace(/&#39;/g, "'")
    .replace(/&nbsp;/gi, " ")
}

function generatedSku(serverId: number, pid: string, taken: Set<string>) {
  const base = `zjmf-${serverId}-${pid}`
    .toLowerCase()
    .replace(/[^a-z0-9_-]+/g, "-")
    .replace(/^-+|-+$/g, "")
    .slice(0, 56) || `zjmf-${serverId}`
  let sku = base
  let index = 1
  while (taken.has(sku)) {
    const suffix = `-${index}`
    sku = `${base.slice(0, 64 - suffix.length)}${suffix}`
    index += 1
  }
  taken.add(sku)
  return sku
}

function mapProductType(value: string | null): ProductType {
  const text = (value ?? "").toLowerCase()
  if (text.includes("server") || text.includes("cloud") || text.includes("dcim") || text.includes("host")) {
    return "server"
  }
  return "other"
}

function cyclesToPricings(config: ZjmfFinanceProductConfig | null): ProductPricingItem[] {
  if (!config) return []
  const seen = new Set<BillingCycle>()
  return config.cycles
    .filter((cycle) => {
      if (seen.has(cycle.cycle)) return false
      seen.add(cycle.cycle)
      return cycle.price !== null
    })
    .map((cycle, index) => ({
      cycle: cycle.cycle,
      price: cycle.price ?? 0,
      setup_fee: cycle.setup_fee ?? 0,
      original_price: null,
      is_enabled: true,
      sort_order: index,
    }))
}

function firstPricing(pricings: ProductPricingItem[]) {
  return pricings.find((item) => item.cycle === "monthly") ?? pricings[0] ?? null
}

function formatCyclePrice(cycle: ZjmfFinanceProductConfig["cycles"][number]) {
  return cycle.price === null ? "未返回价格" : `${cycle.price} ${cycle.currency ?? ""}`.trim()
}

function splitConfigName(value: string | null | undefined) {
  const text = stripHtml(value).trim()
  const index = text.indexOf("|")
  if (index < 0) return { key: text, label: text }
  return {
    key: text.slice(0, index).trim(),
    label: text.slice(index + 1).trim() || text.slice(0, index).trim(),
  }
}

function normalizeKey(value: string | null | undefined) {
  return splitConfigName(value).key.toLowerCase().replace(/\s+/g, "_")
}

function firstNumber(value: string | number | null | undefined) {
  const match = String(value ?? "").match(/-?\d+(?:\.\d+)?/)
  if (!match) return null
  const number = Number.parseFloat(match[0])
  return Number.isFinite(number) ? number : null
}

function toNumber(value: string | number | null | undefined) {
  if (value === null || value === undefined || value === "") return null
  const number = typeof value === "number" ? value : Number.parseFloat(String(value))
  return Number.isFinite(number) ? number : null
}

function truthyFlag(value: string | number | null | undefined) {
  if (typeof value === "number") return value !== 0
  return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase())
}

function formatZjmfStock(config: ZjmfFinanceProductConfig | null) {
  if (!config) return null
  const enabled = truthyFlag(config.product.stock_control)
  const qty = toNumber(config.product.qty)
  if (!enabled) return { label: "库存不限", color: "green" }
  if (qty === null) return { label: "库存已控制，数量未知", color: "orange" }
  return { label: `上游库存 ${qty}`, color: qty > 0 ? "green" : "red" }
}

function zjmfLocalStock(config: ZjmfFinanceProductConfig | null): { policy: "unlimited" | "tracked"; stock: number } {
  if (!config || !truthyFlag(config.product.stock_control)) {
    return { policy: "unlimited", stock: 0 }
  }
  const qty = toNumber(config.product.qty)
  return { policy: "tracked", stock: Math.max(0, Math.floor(qty ?? 0)) }
}

function stockSnapshotFromConfig(config: ZjmfFinanceProductConfig): StockSnapshot {
  const stock = zjmfLocalStock(config)
  const display = formatZjmfStock(config) ?? { label: "库存未知", color: "default" }
  return {
    label: display.label,
    color: display.color,
    stockPolicy: stock.policy,
    stock: stock.stock,
    qty: toNumber(config.product.qty),
    stockControl: truthyFlag(config.product.stock_control),
    hidden: truthyFlag(config.product.hidden),
    retired: truthyFlag(config.product.retired),
    syncedAt: new Date().toISOString(),
    localUpdated: false,
  }
}

function optionByKey(config: ZjmfFinanceProductConfig | null, keys: string[]) {
  const wanted = new Set(keys.map((key) => key.toLowerCase()))
  for (const group of config?.config_groups ?? []) {
    for (const option of group.options ?? []) {
      if (wanted.has(normalizeKey(option.option_name))) {
        return option
      }
    }
  }
  for (const group of config?.config_groups ?? []) {
    for (const option of group.options ?? []) {
      const label = splitConfigName(option.option_name).label.toLowerCase()
      if (keys.some((item) => label.includes(item.toLowerCase()))) {
        return option
      }
    }
  }
  return null
}

function firstSub(option: ZjmfFinanceConfigOption | null) {
  return option?.subs?.[0] ?? null
}

function subValueParts(sub: ReturnType<typeof firstSub>) {
  return splitConfigName(sub?.option_name ?? "")
}

function optionNumber(option: ZjmfFinanceConfigOption | null) {
  const sub = firstSub(option)
  if (!sub) return null
  const parts = subValueParts(sub)
  return (
    firstNumber(parts.key)
    ?? firstNumber(parts.label)
    ?? toNumber(sub.qty_minimum)
    ?? toNumber(sub.qty_maximum)
  )
}

function formatNumber(value: number) {
  return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(2)))
}

function formatMemoryFromOption(option: ZjmfFinanceConfigOption | null, fallbackText: string) {
  const sub = firstSub(option)
  if (sub) {
    const { key, label } = subValueParts(sub)
    const labelMatch = label.match(/(\d+(?:\.\d+)?)\s*(GB|G|MB|M)/i)
    if (labelMatch) {
      const amount = Number.parseFloat(labelMatch[1])
      const unit = labelMatch[2].toUpperCase()
      if (unit === "GB" || unit === "G") return `${formatNumber(amount)}GB`
      if (amount >= 1024 && amount % 1024 === 0) return `${formatNumber(amount / 1024)}GB`
      return `${formatNumber(amount)}MB`
    }
    const raw = firstNumber(key)
    if (raw !== null) {
      if (raw >= 128) return raw % 1024 === 0 ? `${formatNumber(raw / 1024)}GB` : `${formatNumber(raw)}MB`
      return `${formatNumber(raw)}GB`
    }
  }
  const textMatch = fallbackText.match(/(?:内存|memory|ram)[^\d]*(\d+(?:\.\d+)?)\s*(GB|G|MB|M)?/i)
  if (!textMatch) return null
  const amount = Number.parseFloat(textMatch[1])
  const unit = (textMatch[2] ?? "GB").toUpperCase()
  if (unit === "MB" || unit === "M") return amount >= 1024 && amount % 1024 === 0 ? `${formatNumber(amount / 1024)}GB` : `${formatNumber(amount)}MB`
  return `${formatNumber(amount)}GB`
}

function memoryToMb(value: string | null) {
  if (!value) return null
  const match = value.match(/(\d+(?:\.\d+)?)\s*(GB|G|MB|M)?/i)
  if (!match) return null
  const amount = Number.parseFloat(match[1])
  const unit = (match[2] ?? "GB").toUpperCase()
  return Math.round(unit === "MB" || unit === "M" ? amount : amount * 1024)
}

function diskSizeFromOption(option: ZjmfFinanceConfigOption | null) {
  const sub = firstSub(option)
  if (!sub) return null
  const { key, label } = subValueParts(sub)
  return (
    firstNumber(key)
    ?? firstNumber(label)
    ?? toNumber(sub.qty_minimum)
    ?? toNumber(sub.qty_maximum)
  )
}

function bandwidthFromOption(option: ZjmfFinanceConfigOption | null) {
  const value = optionNumber(option)
  return value !== null ? Math.round(value) : null
}

function isNatDesktopProduct(text: string) {
  return /(云电脑|云桌面|挂机宝|nat|remote\s*desktop|desktop)/i.test(text)
}

function findCpuPresetFromText(text: string): CpuPreset {
  const normalized = text.toLowerCase().replace(/\s+/g, " ")
  const exact = CPU_PRESETS.find((preset) => {
    const model = preset.model.toLowerCase()
    const loose = model.replace(/^xeon\s+/, "").replace(/-/g, "[-\\s]*").replace(/\s+/g, "\\s*")
    return normalized.includes(model) || new RegExp(loose, "i").test(text)
  })
  if (exact) return exact
  if (/(epyc|霄龙)/i.test(text)) return CPU_PRESETS.find((preset) => preset.family === "EPYC") ?? CPU_PRESETS[0]
  if (/ryzen|锐龙/i.test(text)) return CPU_PRESETS.find((preset) => preset.family === "Ryzen") ?? CPU_PRESETS[0]
  if (/platinum|铂金/i.test(text)) return CPU_PRESETS.find((preset) => preset.family === "Xeon Platinum") ?? CPU_PRESETS[0]
  if (/gold|金牌/i.test(text)) return CPU_PRESETS.find((preset) => preset.family === "Xeon Gold") ?? CPU_PRESETS[0]
  return CPU_PRESETS.find((preset) => preset.model === "Xeon E5-2690 v4") ?? CPU_PRESETS[0]
}

function inferZjmfSpecs(row: ZjmfFinanceProductItem, config: ZjmfFinanceProductConfig | null) {
  const text = [
    row.name,
    row.type,
    row.group_name,
    row.description,
    config?.product?.name,
    config?.product?.type,
    config?.product?.description,
    ...(config?.config_groups ?? []).map((group) => group.name),
  ].map((item) => stripHtml(item)).join(" ")
  const isNatDesktop = isNatDesktopProduct(text)
  const cpuOption = optionByKey(config, ["cpu", "vcpu", "核心", "处理器"])
  const memoryOption = optionByKey(config, ["memory", "mem", "ram", "内存"])
  const systemDiskOption = optionByKey(config, ["system_disk_size", "system_disk", "系统盘"])
  const dataDiskOption = optionByKey(config, ["data_disk_size", "data_disk", "数据盘"])
  const bwOption = optionByKey(config, ["bw", "out_bw", "bandwidth", "带宽"])
  const inBwOption = optionByKey(config, ["in_bw", "流入", "下行"])

  const vcpu = Math.round(optionNumber(cpuOption) ?? firstNumber(text.match(/(\d+)\s*(?:核|h|c)/i)?.[0]) ?? 0) || null
  const memory = formatMemoryFromOption(memoryOption, text)
  const systemDisk = diskSizeFromOption(systemDiskOption)
  const dataDisk = diskSizeFromOption(dataDiskOption)
  const diskTotal = (systemDisk ?? 0) + (dataDisk ?? 0)
  const disk = diskTotal > 0 ? `${formatNumber(diskTotal)}G SSD` : isNatDesktop ? "Win 50G / Linux 30G" : null
  const bw = bandwidthFromOption(bwOption)
  const inBw = bandwidthFromOption(inBwOption)
  const displayDownBw = inBw ?? (bw && isNatDesktop ? 100 : null)
  const bandwidth = bw && displayDownBw
    ? `${bw}Mbps/${displayDownBw}Mbps`
    : bw ? `${bw}Mbps`
      : displayDownBw ? `${displayDownBw}Mbps`
        : null
  const cpuPreset = findCpuPresetFromText(text)

  return {
    vcpu,
    cpu_model: cpuPreset.model,
    cpu_base_mhz: cpuPreset.baseMhz,
    cpu_turbo_mhz: cpuPreset.turboMhz,
    memory,
    disk,
    bandwidth,
    systemDisk,
    dataDisk,
    bw,
    inBw,
  }
}

function fixedField(key: string, label: string, value: string | number | null | undefined): ConfigField | null {
  if (value === null || value === undefined || value === "") return null
  return { def: `${key}|${label}`, type: "fixed", value: String(value) }
}

function optionFixedField(option: ZjmfFinanceConfigOption | null, fallbackLabel: string): ConfigField | null {
  const sub = firstSub(option)
  if (!option || !sub || option.subs.length !== 1) return null
  const optionName = splitConfigName(option.option_name)
  const subName = subValueParts(sub)
  if (!subName.key && !subName.label) return null
  return fixedField(optionName.key, optionName.label || fallbackLabel, subName.key || subName.label)
}

function optionSelectField(option: ZjmfFinanceConfigOption | null, fallbackLabel: string): ConfigField | null {
  if (!option || option.subs.length <= 1) return optionFixedField(option, fallbackLabel)
  const optionName = splitConfigName(option.option_name)
  const options = option.subs
    .map((sub) => ({ def: sub.option_name ?? "" }))
    .filter((item) => item.def.trim())
  if (options.length === 0) return null
  return { def: `${optionName.key}|${optionName.label || fallbackLabel}`, type: "user_select", options }
}

function buildZjmfProvisionConfig(
  config: ZjmfFinanceProductConfig | null,
  specs: ReturnType<typeof inferZjmfSpecs>,
): ConfigField[] {
  const fields: Array<ConfigField | null> = [
    fixedField("cpu", "CPU核心数", specs.vcpu),
    fixedField("memory", "内存(MB)", memoryToMb(specs.memory)),
    fixedField("system_disk_size", "系统盘(GB)", specs.systemDisk),
    fixedField("data_disk_size", "数据盘(GB)", specs.dataDisk),
    fixedField("bw", "带宽(Mbps)", specs.bw),
    fixedField("in_bw", "流入带宽(Mbps)", specs.inBw),
    optionSelectField(optionByKey(config, ["ip_num", "独立ip", "ip数量", "ipv4"]), "IP数量"),
    optionSelectField(optionByKey(config, ["area", "区域", "数据中心"]), "区域"),
    optionSelectField(optionByKey(config, ["network_type", "网络类型"]), "网络类型"),
    optionSelectField(optionByKey(config, ["ip_group", "防御"]), "防御"),
    optionSelectField(optionByKey(config, ["port", "远程端口"]), "远程端口"),
    optionSelectField(optionByKey(config, ["os", "操作系统"]), "操作系统"),
  ]
  const seen = new Set<string>()
  return fields.filter((field): field is ConfigField => {
    if (!field) return false
    const key = splitConfigName(field.def).key
    if (!key || seen.has(key)) return false
    seen.add(key)
    return true
  })
}

export default function AdminProductIntegration() {
  const navigate = useNavigate()
  const [form] = Form.useForm<{ cloud_server_id: number | null; group_id: number | null; sync_pricing: boolean }>()
  const [loading, setLoading] = useState(true)
  const [upstreamLoading, setUpstreamLoading] = useState(false)
  const [creating, setCreating] = useState(false)
  const [stockSyncing, setStockSyncing] = useState(false)
  const [cloudServers, setCloudServers] = useState<CloudServer[]>([])
  const [groups, setGroups] = useState<ProductGroup[]>([])
  const [products, setProducts] = useState<Product[]>([])
  const [upstreamProducts, setUpstreamProducts] = useState<ZjmfFinanceProductItem[]>([])
  const [stockSnapshots, setStockSnapshots] = useState<Record<string, StockSnapshot>>({})
  const [upstreamMeta, setUpstreamMeta] = useState<{ count: number; currency: string | null } | null>(null)
  const [upstreamError, setUpstreamError] = useState<string | null>(null)
  const [selectedRowKeys, setSelectedRowKeys] = useState<Key[]>([])
  const [keyword, setKeyword] = useState("")
  const [filterMode, setFilterMode] = useState<FilterMode>("unmapped")
  const [previewOpen, setPreviewOpen] = useState(false)
  const [previewLoading, setPreviewLoading] = useState(false)
  const [previewRow, setPreviewRow] = useState<UpstreamRow | null>(null)
  const [previewConfig, setPreviewConfig] = useState<ZjmfFinanceProductConfig | null>(null)
  const [bindState, setBindState] = useState<BindState>({ open: false, row: null, productId: null })
  const [createdResult, setCreatedResult] = useState<CreatedResult | null>(null)

  const cloudServerId = Form.useWatch("cloud_server_id", form)
  const groupId = Form.useWatch("group_id", form)
  const syncPricing = Form.useWatch("sync_pricing", form) ?? true

  const zjmfServers = useMemo(
    () => cloudServers.filter((server) => server.server_type === "zjmf_finance" && !server.disabled),
    [cloudServers],
  )

  const groupMap = useMemo(() => {
    const map = new Map<number, ProductGroup>()
    groups.forEach((group) => map.set(group.id, group))
    return map
  }, [groups])

  const productMap = useMemo(() => {
    const map = new Map<string, Product>()
    products.forEach((product) => {
      if (product.api_type === "zjmf_finance" && product.cloud_server_id && product.api_identifier) {
        map.set(`${product.cloud_server_id}:${product.api_identifier}`, product)
      }
    })
    return map
  }, [products])

  const rows = useMemo<UpstreamRow[]>(
    () =>
      upstreamProducts.map((item) => ({
        ...item,
        mappedProduct: cloudServerId ? productMap.get(`${cloudServerId}:${item.id}`) ?? null : null,
      })),
    [cloudServerId, productMap, upstreamProducts],
  )

  const visibleRows = useMemo(() => {
    const q = keyword.trim().toLowerCase()
    return rows.filter((row) => {
      if (filterMode === "mapped" && !row.mappedProduct) return false
      if (filterMode === "unmapped" && row.mappedProduct) return false
      if (!q) return true
      return [row.id, row.name, row.group_name, row.type]
        .filter(Boolean)
        .some((value) => String(value).toLowerCase().includes(q))
    })
  }, [filterMode, keyword, rows])

  const existingProductOptions = useMemo(
    () =>
      products.map((product) => ({
        value: product.id,
        label: `${product.name} · ${product.sku}`,
      })),
    [products],
  )

  const reloadBase = useCallback(async () => {
    setLoading(true)
    try {
      const [cloudRes, groupRes, productRes] = await Promise.all([
        listCloudServers(),
        listGroups(undefined, { includeOsOptions: false }),
        listProducts(),
      ])
      const nextCloudServers = cloudRes.data ?? []
      const nextGroups = groupRes.data ?? []
      setCloudServers(nextCloudServers)
      setGroups(nextGroups)
      setProducts(productRes.data ?? [])
      form.setFieldsValue({
        cloud_server_id: nextCloudServers.find((item) => item.server_type === "zjmf_finance" && !item.disabled)?.id ?? null,
        group_id: nextGroups[0]?.id ?? null,
        sync_pricing: true,
      })
    } catch (e: unknown) {
      const detail = adminErrorDetail(e, "加载基础数据失败")
      toast.error(detail || "加载基础数据失败")
    } finally {
      setLoading(false)
    }
  }, [form])

  useEffect(() => {
    void reloadBase()
  }, [reloadBase])

  const reloadProducts = useCallback(async () => {
    const res = await listProducts()
    setProducts(res.data ?? [])
  }, [])

  const loadUpstreamProducts = useCallback(async () => {
    if (!cloudServerId) {
      toast.error("请先选择魔方财务节点")
      return
    }
    setUpstreamLoading(true)
    setSelectedRowKeys([])
    setUpstreamError(null)
    try {
      const res = await listZjmfFinanceProducts(cloudServerId)
      const data = res.data
      setUpstreamProducts(data?.products ?? [])
      setStockSnapshots({})
      setUpstreamMeta(data ? { count: data.count, currency: data.currency } : null)
    } catch (e: unknown) {
      const detail = adminErrorDetail(e, "拉取上游商品失败")
      toast.error(detail || "拉取上游商品失败")
      setUpstreamError(detail)
      setUpstreamProducts([])
      setUpstreamMeta(null)
    } finally {
      setUpstreamLoading(false)
    }
  }, [cloudServerId])

  useEffect(() => {
    setUpstreamProducts([])
    setUpstreamMeta(null)
    setUpstreamError(null)
    setSelectedRowKeys([])
    setStockSnapshots({})
  }, [cloudServerId])

  const buildCreatePayload = useCallback(
    async (row: ZjmfFinanceProductItem, takenSkus: Set<string>): Promise<ProductCreatePayload> => {
      let config: ZjmfFinanceProductConfig | null = null
      if (cloudServerId) {
        try {
          const res = await getZjmfFinanceProductConfig(cloudServerId, row.id)
          config = res.data ?? null
        } catch {
          config = null
        }
      }
      const specs = inferZjmfSpecs(row, config)
      const pricings = syncPricing ? cyclesToPricings(config) : []
      const primaryPricing = firstPricing(pricings)
      const localStock = zjmfLocalStock(config)
      return {
        group_id: groupId!,
        sku: generatedSku(cloudServerId!, row.id, takenSkus),
        name: row.name,
        type: mapProductType(row.type),
        status: "draft",
        vcpu: specs.vcpu,
        cpu_model: specs.cpu_model,
        cpu_base_mhz: specs.cpu_base_mhz,
        cpu_turbo_mhz: specs.cpu_turbo_mhz,
        memory: specs.memory,
        disk: specs.disk,
        bandwidth: specs.bandwidth,
        features: stripHtml(config?.product?.description || row.description) || null,
        base_price: primaryPricing?.price ?? 0,
        base_setup_fee: primaryPricing?.setup_fee ?? 0,
        stock_policy: localStock.policy,
        stock: localStock.stock,
        min_quantity: 1,
        max_quantity: 0,
        auto_provision: true,
        cloud_server_id: cloudServerId!,
        api_type: "zjmf_finance",
        api_identifier: row.id,
        provision_config: buildZjmfProvisionConfig(config, specs),
        pricings,
      }
    },
    [cloudServerId, groupId, syncPricing],
  )

  const createRows = useCallback(
    async (targetRows: UpstreamRow[]) => {
      if (!cloudServerId) {
        toast.error("请先选择魔方财务节点")
        return
      }
      if (!groupId) {
        toast.error("请先选择导入到哪个产品组")
        return
      }
      const rowsToCreate = targetRows.filter((row) => !row.mappedProduct)
      if (rowsToCreate.length === 0) {
        toast.error("没有可创建的未绑定商品")
        return
      }
      setCreating(true)
      const takenSkus = new Set(products.map((product) => product.sku))
      let success = 0
      const createdProducts: Product[] = []
      const failures: string[] = []
      try {
        for (const row of rowsToCreate) {
          try {
            const payload = await buildCreatePayload(row, takenSkus)
            const res = await createProduct(payload)
            if (res.data) createdProducts.push(res.data)
            success += 1
          } catch (e: unknown) {
            const detail = (e as { response?: { data?: { detail?: string } } })?.response?.data?.detail
            failures.push(`${row.name}: ${detail || "创建失败"}`)
          }
        }
        await reloadProducts()
        setSelectedRowKeys([])
        if (success > 0) {
          toast.success(`已新建 ${success} 个本地草稿产品`)
          setCreatedResult({ products: createdProducts, groupId, count: success })
        }
        if (failures.length > 0) {
          Modal.warning({
            title: "部分商品创建失败",
            content: (
              <Space direction="vertical">
                {failures.slice(0, 8).map((item) => <Text key={item}>{item}</Text>)}
                {failures.length > 8 && <Text type="secondary">还有 {failures.length - 8} 条未展示</Text>}
              </Space>
            ),
          })
        }
      } finally {
        setCreating(false)
      }
    },
    [buildCreatePayload, cloudServerId, groupId, products, reloadProducts],
  )

  const openPreview = async (row: UpstreamRow) => {
    if (!cloudServerId) return
    setPreviewOpen(true)
    setPreviewLoading(true)
    setPreviewRow(row)
    setPreviewConfig(null)
    try {
      const res = await getZjmfFinanceProductConfig(cloudServerId, row.id)
      setPreviewConfig(res.data ?? null)
    } catch (e: unknown) {
      const detail = (e as { response?: { data?: { detail?: string } } })?.response?.data?.detail
      toast.error(detail || "拉取配置失败")
      setPreviewOpen(false)
      setPreviewRow(null)
    } finally {
      setPreviewLoading(false)
    }
  }

  const closePreview = () => {
    setPreviewOpen(false)
    setPreviewRow(null)
  }

  const bindExistingProduct = async () => {
    if (!cloudServerId || !bindState.row || !bindState.productId) return
    try {
      await updateProduct(bindState.productId, {
        auto_provision: true,
        api_type: "zjmf_finance",
        cloud_server_id: cloudServerId,
        api_identifier: bindState.row.id,
      })
      toast.success("已绑定到现有产品")
      setBindState({ open: false, row: null, productId: null })
      await reloadProducts()
    } catch (e: unknown) {
      const detail = (e as { response?: { data?: { detail?: string } } })?.response?.data?.detail
      toast.error(detail || "绑定失败")
    }
  }

  const selectedRows = useMemo(
    () => rows.filter((row) => selectedRowKeys.includes(row.id)),
    [rows, selectedRowKeys],
  )

  const syncStockRows = useCallback(
    async (targetRows: UpstreamRow[], label: string) => {
      if (!cloudServerId) {
        toast.error("请先选择魔方财务节点")
        return
      }
      const uniqueRows = Array.from(new Map(targetRows.map((row) => [row.id, row])).values())
      if (uniqueRows.length === 0) {
        toast.error("没有可同步的上游商品")
        return
      }

      setStockSyncing(true)
      let fetched = 0
      let updated = 0
      const failures: string[] = []
      try {
        for (const row of uniqueRows) {
          try {
            const res = await getZjmfFinanceProductConfig(cloudServerId, row.id)
            const config = res.data
            if (!config) throw new Error("接口没有返回配置")

            const snapshot = stockSnapshotFromConfig(config)
            let localUpdated = false
            if (row.mappedProduct) {
              const changed =
                row.mappedProduct.stock_policy !== snapshot.stockPolicy ||
                Number(row.mappedProduct.stock ?? 0) !== snapshot.stock
              if (changed) {
                await updateProduct(row.mappedProduct.id, {
                  stock_policy: snapshot.stockPolicy,
                  stock: snapshot.stock,
                })
                localUpdated = true
                updated += 1
              }
            }

            fetched += 1
            setStockSnapshots((prev) => ({
              ...prev,
              [row.id]: { ...snapshot, localUpdated },
            }))
          } catch (e: unknown) {
            const detail =
              (e as { response?: { data?: { detail?: string } } })?.response?.data?.detail ||
              (e as Error)?.message ||
              "同步失败"
            failures.push(`${row.name}: ${detail}`)
            setStockSnapshots((prev) => ({
              ...prev,
              [row.id]: {
                label: "同步失败",
                color: "red",
                stockPolicy: "unlimited",
                stock: 0,
                qty: null,
                stockControl: false,
                hidden: false,
                retired: false,
                syncedAt: new Date().toISOString(),
                localUpdated: false,
                error: detail,
              },
            }))
          }
        }

        if (updated > 0) await reloadProducts()
        if (fetched > 0) {
          toast.success(`${label}完成：已读取 ${fetched} 个，更新本地库存 ${updated} 个`)
        }
        if (failures.length > 0) {
          Modal.warning({
            title: "部分商品库存同步失败",
            content: (
              <Space direction="vertical">
                {failures.slice(0, 8).map((item) => <Text key={item}>{item}</Text>)}
                {failures.length > 8 && <Text type="secondary">还有 {failures.length - 8} 条未展示</Text>}
              </Space>
            ),
          })
        }
      } finally {
        setStockSyncing(false)
      }
    },
    [cloudServerId, reloadProducts],
  )

  const previewSpecs = useMemo(
    () => previewRow && previewConfig ? inferZjmfSpecs(previewRow, previewConfig) : null,
    [previewConfig, previewRow],
  )

  const previewStock = useMemo(
    () => formatZjmfStock(previewConfig),
    [previewConfig],
  )

  const previewCpuPreset = useMemo(
    () => previewSpecs ? CPU_PRESETS.find((preset) => preset.model === previewSpecs.cpu_model) ?? null : null,
    [previewSpecs],
  )

  const previewCpuLabel = previewSpecs ? [
    previewSpecs.cpu_model,
    previewCpuPreset?.family,
    previewCpuPreset ? `${formatGhz(previewCpuPreset.baseMhz)}-${formatGhz(previewCpuPreset.turboMhz)}` : null,
  ].filter(Boolean).join(" · ") : "-"

  const columns: TableColumnsType<UpstreamRow> = [
    {
      title: "上游商品",
      dataIndex: "name",
      render: (value, record) => (
        <Space direction="vertical" size={2}>
          <Space wrap>
            <Text strong>{value}</Text>
            <Tag>PID {record.id}</Tag>
            {record.type && <Tag>{record.type}</Tag>}
          </Space>
          <Text type="secondary" style={{ fontSize: 12 }}>
            {stripHtml(record.description).slice(0, 90) || "无描述"}
          </Text>
        </Space>
      ),
    },
    {
      title: "上游分组",
      dataIndex: "group_name",
      width: 180,
      render: (value) => value ? <Tag>{value}</Tag> : "-",
    },
    {
      title: "上游库存",
      width: 190,
      render: (_, record) => {
        const snapshot = stockSnapshots[record.id]
        const localProduct = record.mappedProduct
        const localLabel = localProduct
          ? localProduct.stock_policy === "tracked" ? `本地库存 ${localProduct.stock}` : "本地不限库存"
          : null
        const mismatch = Boolean(
          snapshot &&
          localProduct &&
          (localProduct.stock_policy !== snapshot.stockPolicy || Number(localProduct.stock ?? 0) !== snapshot.stock),
        )
        return (
          <Space direction="vertical" size={2}>
            {snapshot ? (
              <>
                <Space wrap size={4}>
                  <Tag color={snapshot.color}>{snapshot.label}</Tag>
                  {snapshot.hidden && <Tag color="orange">上游隐藏</Tag>}
                  {snapshot.retired && <Tag color="red">上游停售</Tag>}
                  {snapshot.localUpdated && <Tag color="blue">已更新本地</Tag>}
                </Space>
                {snapshot.error && <Text type="danger" style={{ fontSize: 12 }}>{snapshot.error}</Text>}
              </>
            ) : (
              <Tag color="default">未同步</Tag>
            )}
            {localLabel && <Tag color={mismatch ? "orange" : "default"}>{localLabel}</Tag>}
          </Space>
        )
      },
    },
    {
      title: "对接状态",
      width: 220,
      render: (_, record) => record.mappedProduct ? (
        <Space direction="vertical" size={2}>
          <Tag color="green">已绑定</Tag>
          <Button type="link" size="small" onClick={() => navigate(`/admin/products/${record.mappedProduct!.id}`)}>
            {record.mappedProduct.name}
          </Button>
        </Space>
      ) : (
        <Tag color="default">未绑定</Tag>
      ),
    },
    {
      title: "操作",
      width: 280,
      align: "right",
      render: (_, record) => (
        <Space wrap>
          <Button size="small" icon={<EyeOutlined />} onClick={() => void openPreview(record)}>
            预览
          </Button>
          {record.mappedProduct ? (
            <Button size="small" icon={<EditOutlined />} onClick={() => navigate(`/admin/products/${record.mappedProduct!.id}`)}>
              编辑
            </Button>
          ) : (
            <>
              <Button size="small" icon={<LinkOutlined />} onClick={() => setBindState({ open: true, row: record, productId: null })}>
                绑定已有
              </Button>
              <Popconfirm
                title={`新建本地草稿产品「${record.name}」？`}
                okText="新建"
                cancelText="取消"
                onConfirm={() => void createRows([record])}
              >
                <Button size="small" type="primary" icon={<PlusOutlined />}>
                  新建草稿
                </Button>
              </Popconfirm>
            </>
          )}
        </Space>
      ),
    },
  ]

  return (
    <PageSkeleton loading={loading} spec={PAGE_SKELETON} gap={24}>
      <Space direction="vertical" size={18} style={{ width: "100%" }}>
        <Row gutter={[16, 16]} align="bottom" justify="space-between">
          <Col>
            <Title level={3} style={{ margin: 0 }}>商品对接</Title>
            <Paragraph type="secondary" style={{ marginTop: 8, marginBottom: 0 }}>
              从魔方财务拉取上游商品，快速新建本地草稿或绑定到已有产品。
            </Paragraph>
          </Col>
          <Col>
            <Space wrap>
              <Button icon={<ReloadOutlined />} onClick={() => void reloadBase()} loading={loading}>
                刷新基础数据
              </Button>
              <Button
                type="primary"
                icon={<CloudDownloadOutlined />}
                loading={upstreamLoading}
                onClick={() => void loadUpstreamProducts()}
              >
                拉取上游商品
              </Button>
            </Space>
          </Col>
        </Row>

        <Alert
          type="warning"
          showIcon
          message="这里不是上架页面，是把魔方财务商品接进本地产品库"
          description="新建本地草稿：生成本地产品、同步基础价格、绑定上游 PID；绑定已有产品：不新增产品，只写入绑定关系。完整的自动下单/开通还需要后端开通链路继续接入魔方财务订单接口。"
        />

        {upstreamError && (
          <Alert
            type="error"
            showIcon
            closable
            onClose={() => setUpstreamError(null)}
            message="上游魔方财务连接失败"
            description={upstreamError}
          />
        )}

        <Card size="small">
          <Form form={form} layout="vertical" initialValues={{ sync_pricing: true }}>
            <Row gutter={16} align="bottom">
              <Col xs={24} lg={8}>
                <Form.Item label="魔方财务节点" name="cloud_server_id">
                  <Select
                    allowClear
                    showSearch
                    optionFilterProp="label"
                    placeholder="选择已配置的魔方财务节点"
                    options={zjmfServers.map((server) => ({
                      value: server.id,
                      label: `${server.name} · ${server.account_type === "resource" ? "资源池" : "客户账号"}`,
                    }))}
                  />
                </Form.Item>
              </Col>
              <Col xs={24} lg={8}>
                <Form.Item label="新建到产品组" name="group_id">
                  <Select
                    allowClear
                    showSearch
                    optionFilterProp="label"
                    placeholder="新建本地产品时使用"
                    options={groups.map((group) => ({ value: group.id, label: group.name }))}
                  />
                </Form.Item>
              </Col>
              <Col xs={24} lg={4}>
                <Form.Item label="同步上游价格" name="sync_pricing" valuePropName="checked">
                  <Switch checkedChildren="同步" unCheckedChildren="跳过" />
                </Form.Item>
              </Col>
              <Col xs={24} lg={4}>
                <Button
                  block
                  type="primary"
                  icon={<CloudDownloadOutlined />}
                  loading={upstreamLoading}
                  onClick={() => void loadUpstreamProducts()}
                >
                  拉取
                </Button>
              </Col>
            </Row>
          </Form>

          <Space wrap>
            {upstreamMeta && <Tag>上游商品 {upstreamMeta.count}</Tag>}
            {upstreamMeta?.currency && <Tag>{upstreamMeta.currency}</Tag>}
            <Tag>本地已绑定 {rows.filter((row) => row.mappedProduct).length}</Tag>
            <Tag>可新建 {rows.filter((row) => !row.mappedProduct).length}</Tag>
            <Tag>创建位置：{groupId ? groupMap.get(groupId)?.name ?? `产品组 ${groupId}` : "未选择"}</Tag>
          </Space>
        </Card>

        <Card size="small">
          <Space direction="vertical" size={14} style={{ width: "100%" }}>
            <Row gutter={[12, 12]} justify="space-between">
              <Col xs={24} lg={12}>
                <Space wrap>
                  <Select<FilterMode>
                    value={filterMode}
                    style={{ width: 132 }}
                    options={[
                      { value: "unmapped", label: "未绑定" },
                      { value: "mapped", label: "已绑定" },
                      { value: "all", label: "全部" },
                    ]}
                    onChange={setFilterMode}
                  />
                  <Input.Search
                    allowClear
                    placeholder="搜索 PID、名称、分组"
                    style={{ width: 280 }}
                    value={keyword}
                    onChange={(event) => setKeyword(event.target.value)}
                  />
                </Space>
              </Col>
              <Col>
                <Space wrap>
                  <Button
                    icon={<ReloadOutlined />}
                    disabled={selectedRows.length === 0}
                    loading={stockSyncing}
                    onClick={() => void syncStockRows(selectedRows, "选中库存同步")}
                  >
                    同步选中库存
                  </Button>
                  <Button
                    icon={<ReloadOutlined />}
                    disabled={rows.filter((row) => row.mappedProduct).length === 0}
                    loading={stockSyncing}
                    onClick={() => void syncStockRows(rows.filter((row) => row.mappedProduct), "已绑定库存同步")}
                  >
                    同步已绑定库存
                  </Button>
                  <Button
                    icon={<ReloadOutlined />}
                    disabled={rows.length === 0}
                    loading={stockSyncing}
                    onClick={() => void syncStockRows(rows, "全部库存同步")}
                  >
                    同步全部库存
                  </Button>
                  <Button
                    icon={<PlusOutlined />}
                    type="primary"
                    disabled={selectedRows.filter((row) => !row.mappedProduct).length === 0}
                    loading={creating}
                    onClick={() => void createRows(selectedRows)}
                  >
                    批量新建草稿
                  </Button>
                </Space>
              </Col>
            </Row>

            <Table
              rowKey="id"
              columns={columns}
              dataSource={visibleRows}
              loading={upstreamLoading || creating}
              rowSelection={{
                selectedRowKeys,
                getCheckboxProps: (record) => ({ disabled: !!record.mappedProduct }),
                onChange: setSelectedRowKeys,
              }}
              pagination={{ pageSize: 20, showSizeChanger: true }}
              locale={{ emptyText: <Empty description="请选择节点并拉取上游商品" /> }}
              scroll={{ x: 1200 }}
            />
          </Space>
        </Card>
      </Space>

      <Modal
        open={previewOpen}
        title="上游配置预览"
        width={820}
        onCancel={closePreview}
        footer={<Button type="primary" onClick={closePreview}>关闭</Button>}
      >
        {previewLoading ? (
          <Alert type="info" showIcon message="正在拉取上游配置" />
        ) : previewConfig ? (
          <Space direction="vertical" size={14} style={{ width: "100%" }}>
            <Alert
              type="success"
              showIcon
              message={previewConfig.product.name || "上游商品"}
              description={stripHtml(previewConfig.product.description) || "无描述"}
            />
            <Space wrap>
              {previewStock && <Tag color={previewStock.color}>{previewStock.label}</Tag>}
              {truthyFlag(previewConfig.product.hidden) && <Tag color="orange">上游隐藏</Tag>}
              {truthyFlag(previewConfig.product.retired) && <Tag color="red">上游停售</Tag>}
              <Tag>周期 {previewConfig.cycles.length}</Tag>
              <Tag>配置组 {previewConfig.config_group_count}</Tag>
              <Tag>配置项 {previewConfig.config_option_count}</Tag>
              <Tag>自定义字段 {previewConfig.customfield_count}</Tag>
            </Space>
            {previewSpecs && (
              <Descriptions
                bordered
                size="small"
                title="将写入本地产品的规格"
                column={{ xs: 1, sm: 2 }}
              >
                <Descriptions.Item label="vCPU">{previewSpecs.vcpu ?? "-"}</Descriptions.Item>
                <Descriptions.Item label="CPU 型号">{previewCpuLabel}</Descriptions.Item>
                <Descriptions.Item label="主频 MHz">{previewSpecs.cpu_base_mhz ?? "-"}</Descriptions.Item>
                <Descriptions.Item label="睿频 MHz">{previewSpecs.cpu_turbo_mhz ?? "-"}</Descriptions.Item>
                <Descriptions.Item label="内存">{previewSpecs.memory ?? "-"}</Descriptions.Item>
                <Descriptions.Item label="硬盘">{previewSpecs.disk ?? "-"}</Descriptions.Item>
                <Descriptions.Item label="带宽">{previewSpecs.bandwidth ?? "-"}</Descriptions.Item>
              </Descriptions>
            )}
            {previewConfig.cycles.length > 0 && (
              <Card size="small" title="价格周期">
                <Space wrap>
                  {previewConfig.cycles.map((cycle) => (
                    <Tag key={`${cycle.cycle}-${cycle.currency ?? ""}`} color={cycle.price === null ? "default" : undefined}>
                      {cycle.label} {formatCyclePrice(cycle)}
                      {cycle.setup_fee ? ` + ${cycle.setup_fee}` : ""}
                    </Tag>
                  ))}
                </Space>
              </Card>
            )}
            {previewConfig.config_groups.length > 0 && (
              <Card size="small" title="配置项">
                <Space direction="vertical" style={{ width: "100%" }}>
                  {previewConfig.config_groups.slice(0, 6).map((group, groupIndex) => (
                    <div key={group.id ?? `group-${groupIndex}`}>
                      <Text strong>{group.name || "未命名配置组"}</Text>
                      <div style={{ marginTop: 6 }}>
                        <Space wrap>
                          {group.options.slice(0, 12).map((option, optionIndex) => (
                            <Tag key={option.id ?? `option-${groupIndex}-${optionIndex}`}>
                              {option.option_name || "配置项"} × {option.sub_count}
                            </Tag>
                          ))}
                        </Space>
                      </div>
                    </div>
                  ))}
                </Space>
              </Card>
            )}
          </Space>
        ) : (
          <Alert type="info" showIcon message="暂无配置数据" />
        )}
      </Modal>

      <Modal
        open={!!createdResult}
        title="已新建本地草稿"
        width={720}
        onCancel={() => setCreatedResult(null)}
        footer={[
          <Button key="continue" onClick={() => setCreatedResult(null)}>
            继续对接
          </Button>,
          <Button
            key="products"
            icon={<LinkOutlined />}
            onClick={() => {
              if (!createdResult) return
              const targetGroupId = createdResult.groupId
              setCreatedResult(null)
              navigate(`/admin/products?group_id=${targetGroupId}`)
            }}
          >
            去产品管理查看
          </Button>,
          <Button
            key="edit"
            type="primary"
            icon={<EditOutlined />}
            disabled={!createdResult?.products[0]}
            onClick={() => {
              const firstProductId = createdResult?.products[0]?.id
              setCreatedResult(null)
              if (firstProductId) navigate(`/admin/products/${firstProductId}`)
            }}
          >
            编辑第一个产品
          </Button>,
        ]}
      >
        {createdResult && (
          <Space direction="vertical" size={14} style={{ width: "100%" }}>
            <Alert
              type="success"
              showIcon
              message={`已创建 ${createdResult.count} 个草稿产品`}
              description={`创建位置：${groupMap.get(createdResult.groupId)?.name ?? `产品组 ${createdResult.groupId}`}。草稿不会自动上架，需要编辑确认后再发布。`}
            />
            {createdResult.products.length > 0 ? (
              <Space direction="vertical" size={8} style={{ width: "100%" }}>
                {createdResult.products.map((product) => (
                  <Row key={product.id} gutter={[12, 12]} align="middle" justify="space-between">
                    <Col flex="auto">
                      <Space wrap>
                        <Text strong>{product.name}</Text>
                        <Tag>{product.sku}</Tag>
                        <Tag color="gold">草稿</Tag>
                      </Space>
                    </Col>
                    <Col>
                      <Button
                        size="small"
                        icon={<EditOutlined />}
                        onClick={() => {
                          setCreatedResult(null)
                          navigate(`/admin/products/${product.id}`)
                        }}
                      >
                        编辑
                      </Button>
                    </Col>
                  </Row>
                ))}
              </Space>
            ) : (
              <Alert type="info" showIcon message="接口没有返回产品详情，可以直接去产品管理查看刚创建的草稿。" />
            )}
          </Space>
        )}
      </Modal>

      <Modal
        open={bindState.open}
        title="绑定到已有产品"
        okText="绑定已有产品"
        cancelText="取消"
        onOk={() => void bindExistingProduct()}
        onCancel={() => setBindState({ open: false, row: null, productId: null })}
        okButtonProps={{ disabled: !bindState.productId }}
      >
        <Space direction="vertical" size={12} style={{ width: "100%" }}>
          <Alert
            type="info"
            showIcon
            message={bindState.row ? `把本地已有产品接到：${bindState.row.name}` : "请选择上游商品"}
            description={bindState.row ? `不会新增产品，只会把所选本地产品绑定到上游 PID ${bindState.row.id}` : undefined}
          />
          <Select
            showSearch
            optionFilterProp="label"
            placeholder="选择要绑定的本地产品"
            style={{ width: "100%" }}
            value={bindState.productId ?? undefined}
            options={existingProductOptions}
            onChange={(productId) => setBindState((state) => ({ ...state, productId }))}
          />
        </Space>
      </Modal>
    </PageSkeleton>
  )
}
