import {
  DETAIL_TITLES,
  DOMAIN_LABELS,
  EXPENSE_RULE_BLOCK_PATTERN,
  JSON_RISK_DETAIL_MODE,
  LEGACY_RISK_SCENARIO_KEYS,
  PREVIEW_RULE_CODE,
  PREVIEW_RULE_ID,
  PREVIEW_RULE_VERSION_SPECS,
  REVIEW_META,
  RISK_SCENARIO_VALUES,
  RULE_SPREADSHEET_BLOCK_PATTERN,
  RULE_TAB_TAG_ALIASES,
  RULE_TEMPLATE_LABELS,
  SCENARIO_LABELS,
  SPREADSHEET_DETAIL_MODE,
  STATUS_META,
  TAB_META,
  TYPE_META,
  VERSION_STATE_META
} from './auditViewMetadata.js'
import {
  buildRiskRuleFieldSummary,
  formatRiskRuleAge,
  resolveRiskRuleBusinessDescription,
  resolveRiskRuleCreatedAt,
  resolveRiskRuleFields,
  resolveRiskRuleFlow,
  resolveRiskRuleFlowDiagramSvg,
  resolveRiskRuleScore,
  resolveRiskRuleScoreDetail,
  resolveRiskRuleScoreLabel,
  resolveRiskRuleScoreLevel,
  resolveRiskRuleSeverity,
  resolveRiskRuleSeverityLabel
} from './auditViewRiskRuleModel.js'

const EXPENSE_TYPE_SCENARIO_LABELS = {
  travel: '差旅费',
  hotel: '住宿费',
  transport: '交通费',
  meal: '业务招待费',
  meeting: '会务费',
  marketing: '市场推广费',
  office: '办公用品费',
  training: '培训费',
  software: '软件服务费',
  communication: '通信费',
  welfare: '福利费'
}

export {
  DETAIL_TITLES,
  DOMAIN_LABELS,
  ENABLED_STATE_OPTIONS,
  EXPENSE_RULE_BLOCK_PATTERN,
  JSON_RISK_DETAIL_MODE,
  LEGACY_RISK_SCENARIO_KEYS,
  PREVIEW_RULE_CODE,
  PREVIEW_RULE_ID,
  PREVIEW_RULE_VERSION_SPECS,
  REVIEW_META,
  RISK_SCENARIO_OPTIONS,
  RISK_SCENARIO_VALUES,
  RISK_RULE_TABLE_COLUMNS,
  RULE_SPREADSHEET_BLOCK_PATTERN,
  RULE_TABLE_COLUMNS,
  RULE_TAB_TAG_ALIASES,
  RULE_TEMPLATE_LABELS,
  SCENARIO_LABELS,
  SPREADSHEET_DETAIL_MODE,
  STATUS_META,
  STATUS_OPTIONS,
  ONLINE_STATE_OPTIONS,
  TAB_META,
  TYPE_META,
  VERSION_STATE_META
} from './auditViewMetadata.js'

export function buildPreviewSpreadsheetMeta(spec) {
  return {
    file_name: spec.fileName,
    storage_key: `preview/agent-assets/${PREVIEW_RULE_ID}/${spec.version}/${encodeURIComponent(spec.fileName)}`,
    mime_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    size_bytes: 82416,
    checksum: `preview-${spec.version.replace(/\./g, '-')}`,
    updated_at: spec.updatedAt,
    updated_by: spec.updatedBy,
    source: spec.source
  }
}

export function buildPreviewSpreadsheetVersionMarkdown(spec) {
  const metadata = buildPreviewSpreadsheetMeta(spec)
  return [
    '# 公司差旅费报销规则',
    '',
    '## 规则载体',
    '',
    '- 页面状态：前端预览',
    `- 当前规则版本：\`${spec.version}\``,
    `- 表格文件：\`${spec.fileName}\``,
    `- 最近更新人：${spec.updatedBy}`,
    `- 最近更新时间：${spec.updatedAt}`,
    '',
    '## 说明',
    '',
    '- 当前环境暂无真实规则文件，先用于展示 Excel 规则详情页布局。',
    '- 真实上传、编辑与版本回写逻辑会在接好正式数据后启用。',
    '',
    '```rule-spreadsheet',
    JSON.stringify(metadata, null, 2),
    '```'
  ].join('\n')
}

export function createPreviewRuleDetailPayload() {
  const recentVersions = PREVIEW_RULE_VERSION_SPECS.map((spec, index) => ({
    id: `${PREVIEW_RULE_ID}-version-${index + 1}`,
    asset_id: PREVIEW_RULE_ID,
    version: spec.version,
    content: buildPreviewSpreadsheetVersionMarkdown(spec),
    content_type: 'markdown',
    change_note: spec.note,
    created_by: spec.updatedBy,
    created_at: spec.updatedAt,
    is_current: spec.isCurrent
  }))
  const currentSpec = PREVIEW_RULE_VERSION_SPECS[0]
  const currentMeta = buildPreviewSpreadsheetMeta(currentSpec)

  return {
    id: PREVIEW_RULE_ID,
    asset_type: 'rule',
    code: PREVIEW_RULE_CODE,
    name: '公司差旅费报销规则',
    description: '前端预览态：先展示 Excel 规则详情页布局、版本卡片和编辑入口位置。',
    domain: 'expense',
    scenario_json: ['差旅'],
    owner: '财务制度管理组',
    reviewer: '顾承宇',
    status: 'active',
    current_version: currentSpec.version,
    published_version: currentSpec.version,
    working_version: currentSpec.version,
    config_json: {
      severity: 'medium',
      enabled: true,
      tag: '财务规则',
      detail_mode: 'spreadsheet',
      runtime_kind: 'travel_policy',
      scenario_category: '差旅',
      ai_review_category: '差旅',
      rule_template_label: '差旅报销 Excel 模板',
      rule_document: {
        ...currentMeta,
        asset_version: currentSpec.version
      }
    },
    created_at: '2026-05-10T11:10:00Z',
    updated_at: currentSpec.updatedAt,
    current_version_content: recentVersions[0].content,
    current_version_content_type: 'markdown',
    current_version_change_note: currentSpec.note,
    recent_versions: recentVersions,
    latest_review: {
      id: `${PREVIEW_RULE_ID}-review-1`,
      asset_id: PREVIEW_RULE_ID,
      version: currentSpec.version,
      reviewer: '顾承宇',
      review_status: 'approved',
      review_note: '当前为页面预览态，先确认布局与交互位置。',
      reviewed_at: '2026-05-17T10:00:00Z',
      created_at: '2026-05-17T10:00:00Z'
    }
  }
}

export function buildPreviewRuleListItem() {
  const payload = createPreviewRuleDetailPayload()
  return {
    ...buildListItem(payload),
    isPreviewMock: true
  }
}

export function buildPreviewRuleDetail() {
  const detail = buildDetailViewModel(createPreviewRuleDetailPayload(), [])
  return {
    ...detail,
    isPreviewMock: true
  }
}

export function normalizeText(value) {
  return String(value || '').trim()
}

export function isPlainObject(value) {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
}

export function readConfigJson(value) {
  if (isPlainObject(value?.configJson)) {
    return value.configJson
  }
  if (isPlainObject(value?.config_json)) {
    return value.config_json
  }
  return {}
}

export function resolveRiskRuleEnabled(source, rulePayload = null) {
  const configJson = readConfigJson(source)
  if (isPlainObject(rulePayload) && rulePayload.enabled === false) {
    return false
  }
  if (source?.enabled === false || configJson.enabled === false) {
    return false
  }
  return true
}

const LAST_OPERATION_LABELS = {
  generate: '开始生成',
  create: '创建',
  test: '测试',
  online: '上线',
  offline: '下线',
  delete: '删除',
  update: '更新'
}

const RISK_RULE_BUSINESS_STAGE_LABELS = {
  expense_application: '费用申请',
  reimbursement: '费用报销'
}

function resolveRiskRuleBusinessStage(source, rulePayload = null) {
  const configJson = readConfigJson(source)
  const metadata = rulePayload && typeof rulePayload === 'object' ? rulePayload.metadata || {} : {}
  const stage =
    normalizeText(configJson.business_stage) ||
    normalizeText(metadata.business_stage) ||
    normalizeText(rulePayload?.business_stage)
  const label =
    normalizeText(configJson.business_stage_label) ||
    normalizeText(metadata.business_stage_label) ||
    RISK_RULE_BUSINESS_STAGE_LABELS[stage]
  return {
    value: stage || 'reimbursement',
    label: label || '费用报销'
  }
}

function resolveRiskRuleOnlineMeta(statusValue) {
  if (statusValue === 'active') {
    return { label: '已上线', tone: 'success', online: true }
  }
  if (statusValue === 'disabled') {
    return { label: '已下线', tone: 'disabled', online: false }
  }
  if (statusValue === 'generating') {
    return { label: '生成中', tone: 'info', online: false }
  }
  if (statusValue === 'failed') {
    return { label: '生成失败', tone: 'danger', online: false }
  }
  return { label: '待上线', tone: 'draft', online: false }
}

function resolveLastOperationLabel(source, fallback = {}) {
  const configJson = readConfigJson(source)
  const operation = isPlainObject(configJson.last_operation) ? configJson.last_operation : {}
  const action = normalizeText(operation.action) || normalizeText(fallback.action) || 'create'
  const actor = normalizeText(operation.actor) || normalizeText(fallback.actor) || '系统'
  const at = normalizeText(operation.at) || normalizeText(fallback.at)
  const actionLabel = LAST_OPERATION_LABELS[action] || action
  const timeLabel = formatDateTime(at)
  return timeLabel && timeLabel !== '-' ? `${actionLabel}：${actor} · ${timeLabel}` : `${actionLabel}：${actor}`
}

export function readRuleDocumentMeta(value) {
  const configJson = readConfigJson(value)
  return isPlainObject(configJson.rule_document) ? configJson.rule_document : null
}

export function isSpreadsheetRuleSource(value) {
  const configJson = readConfigJson(value)
  return normalizeText(configJson.detail_mode || configJson.rule_detail_mode).toLowerCase() === SPREADSHEET_DETAIL_MODE
}

export function isJsonRiskRuleSource(value) {
  const configJson = readConfigJson(value)
  return normalizeText(configJson.detail_mode || configJson.rule_detail_mode).toLowerCase() === JSON_RISK_DETAIL_MODE
}

export function normalizeRuleTagValue(value) {
  return normalizeText(value).toLowerCase().replace(/[\s_-]+/g, '')
}

export function collectRuleTagValues(source) {
  const configJson = readConfigJson(source)
  const rawValues = [
    configJson.tag,
    configJson.rule_tag,
    ...(Array.isArray(configJson.tags) ? configJson.tags : []),
    ...(Array.isArray(configJson.rule_tags) ? configJson.rule_tags : [])
  ]

  return rawValues.map((item) => normalizeText(item)).filter(Boolean)
}

export function resolveRuleTabId(source) {
  const code = normalizeText(source?.code || '').toLowerCase()
  if (code.startsWith('risk.')) {
    return 'riskRules'
  }
  if (isJsonRiskRuleSource(source)) {
    return 'riskRules'
  }

  const normalizedTags = collectRuleTagValues(source).map((item) => normalizeRuleTagValue(item))

  if (normalizedTags.some((item) => RULE_TAB_TAG_ALIASES.riskRules.has(item))) {
    return 'riskRules'
  }
  if (normalizedTags.some((item) => RULE_TAB_TAG_ALIASES.financialRules.has(item))) {
    return 'financialRules'
  }
  return ''
}

export function resolveTabId(source, typeKey) {
  if (typeKey === 'rules') {
    return resolveRuleTabId(source)
  }
  return typeKey
}

export function resolveTabMeta(tabId, typeKey) {
  if (TAB_META[tabId]) {
    return TAB_META[tabId]
  }
  if (typeKey === 'rules') {
    return {
      ...TYPE_META.rules,
      typeKey: 'rules',
      badgeTone: 'primary'
    }
  }
  return TAB_META[typeKey]
}

export function resolveRiskRuleDescription(payload) {
  if (!isPlainObject(payload)) {
    return ''
  }
  return normalizeText(payload.description)
}

export function resolveRiskRuleSourceRef(payload) {
  if (!isPlainObject(payload)) {
    return ''
  }
  const metadata = isPlainObject(payload.metadata) ? payload.metadata : {}
  return normalizeText(metadata.source_ref)
}

export function inferRiskCategoryFromCode(code) {
  const normalized = normalizeText(code).toLowerCase()
  if (normalized.startsWith('risk.travel.')) {
    return '差旅'
  }
  if (normalized.startsWith('risk.invoice.')) {
    return '发票'
  }
  if (normalized.includes('entertainment') || normalized.includes('meal_localized')) {
    return '餐饮招待'
  }
  if (normalized.includes('consecutive_transport')) {
    return '交通出行'
  }
  if (normalized.startsWith('risk.expense.')) {
    return '费用科目'
  }
  return '通用'
}

export function normalizeRiskScenarioCategory(value) {
  const normalized = normalizeText(value)
  const alias = normalized === '通讯费' ? '通信费' : normalized
  return RISK_SCENARIO_VALUES.has(alias) ? alias : ''
}

export function normalizeExpenseTypeScenarioLabels(value) {
  const values = Array.isArray(value) ? value : normalizeText(value) ? [value] : []
  const labels = []
  const seen = new Set()

  values.forEach((item) => {
    const key = normalizeText(item).toLowerCase()
    const label = EXPENSE_TYPE_SCENARIO_LABELS[key] || normalizeRiskScenarioCategory(item)
    if (!label || seen.has(label)) {
      return
    }
    seen.add(label)
    labels.push(label)
  })

  return labels
}

export function readRiskRuleExpenseTypes(source) {
  const configJson = readConfigJson(source)
  const metadata = isPlainObject(configJson.metadata) ? configJson.metadata : {}
  const appliesTo = isPlainObject(configJson.applies_to) ? configJson.applies_to : {}
  const values = []

  ;[
    configJson.expense_types,
    metadata.expense_types,
    appliesTo.expense_types,
    source?.expense_types
  ].forEach((item) => {
    if (Array.isArray(item)) {
      values.push(...item)
    } else if (normalizeText(item)) {
      values.push(item)
    }
  })

  return values
}

export function readScenarioItems(source) {
  if (Array.isArray(source?.scenario_json)) {
    return source.scenario_json
  }
  if (Array.isArray(source?.scenarioList)) {
    return source.scenarioList
  }
  return []
}

export function resolveRiskRuleCategory(source) {
  const configJson = readConfigJson(source)
  const expenseScenarioLabels = normalizeExpenseTypeScenarioLabels(readRiskRuleExpenseTypes(source))
  if (expenseScenarioLabels.length) {
    return formatScenarioList(expenseScenarioLabels)
  }

  const expenseCategoryLabel =
    normalizeText(configJson.expense_category_label) ||
    normalizeText(configJson.metadata?.expense_category_label) ||
    normalizeText(source?.expense_category_label)
  if (expenseCategoryLabel) {
    return expenseCategoryLabel
  }

  const explicit = normalizeRiskScenarioCategory(configJson.risk_category)
  if (explicit) {
    return explicit
  }

  const payloadCategory = normalizeRiskScenarioCategory(source?.risk_category)
  if (payloadCategory) {
    return payloadCategory
  }

  const scenarioItems = readScenarioItems(source)
  const businessScenario = scenarioItems
    .map((item) => normalizeText(item))
    .find((item) => item && !LEGACY_RISK_SCENARIO_KEYS.has(item) && RISK_SCENARIO_VALUES.has(item))
  if (businessScenario) {
    return businessScenario
  }

  return inferRiskCategoryFromCode(source?.code)
}

export function inferFinancialRuleCategory(source) {
  const configJson = readConfigJson(source)
  const explicit =
    normalizeRiskScenarioCategory(configJson.scenario_category) ||
    normalizeRiskScenarioCategory(configJson.ai_review_category) ||
    normalizeRiskScenarioCategory(configJson.risk_category) ||
    normalizeRiskScenarioCategory(source?.scenario_category) ||
    normalizeRiskScenarioCategory(source?.risk_category)
  if (explicit) {
    return explicit
  }

  const scenarioCategory = readScenarioItems(source)
    .map((item) => normalizeRiskScenarioCategory(item))
    .find(Boolean)
  if (scenarioCategory) {
    return scenarioCategory
  }

  const configRuntimeRule = isPlainObject(configJson.runtime_rule) ? configJson.runtime_rule : {}
  const haystack = [
    source?.code,
    source?.name,
    source?.description,
    configJson.runtime_kind,
    configRuntimeRule.kind,
    configRuntimeRule.scenario,
    configRuntimeRule.template_key,
    ...readScenarioItems(source)
  ]
    .map((item) => normalizeText(item).toLowerCase())
    .filter(Boolean)
    .join(' ')

  if (!haystack) {
    return '通用'
  }
  if (/(travel|trip|差旅|出差|住宿|酒店)/i.test(haystack)) {
    return '差旅'
  }
  if (/(invoice|receipt|attachment|票据|发票|单据|附件)/i.test(haystack)) {
    return '发票'
  }
  if (/(meal|dining|entertainment|餐饮|招待|餐费|用餐)/i.test(haystack)) {
    return '餐饮招待'
  }
  if (/(transport|traffic|taxi|交通|出行|打车|机票|火车|高铁|地铁|公交)/i.test(haystack)) {
    return '交通出行'
  }
  if (/(office|material|suppl|办公|物料|耗材)/i.test(haystack)) {
    return '办公物料'
  }
  if (/(communication|telecom|phone|通信|通讯|手机)/i.test(haystack)) {
    return '通信费'
  }
  if (/(welfare|福利)/i.test(haystack)) {
    return '福利费'
  }
  if (/(expense_standard|费用科目|费用标准|补贴|科目)/i.test(haystack)) {
    return '费用科目'
  }
  return '通用'
}

export function resolveRuleScenarioCategory(source, tabId = '') {
  const scenarioList = resolveRuleScenarioList(source, tabId)
  if (scenarioList.length) {
    return formatScenarioList(scenarioList)
  }
  return ''
}

export function resolveRuleScenarioList(source, tabId = '') {
  const resolvedTabId = tabId || resolveRuleTabId(source)
  if (resolvedTabId === 'riskRules' || isJsonRiskRuleSource(source)) {
    const expenseScenarioLabels = normalizeExpenseTypeScenarioLabels(readRiskRuleExpenseTypes(source))
    if (expenseScenarioLabels.length) {
      return expenseScenarioLabels
    }
    const riskCategory = resolveRiskRuleCategory(source)
    return riskCategory ? [riskCategory] : []
  }
  if (resolvedTabId === 'financialRules') {
    const financialCategory = inferFinancialRuleCategory(source)
    return financialCategory ? [financialCategory] : []
  }
  return []
}

export function buildRiskListSubtitle(text, maxLength = 42) {
  const normalized = normalizeText(text)
  if (!normalized) {
    return '平台内置风险规则'
  }
  const firstSentence = normalized.split(/[。；;！？\n]/)[0] || normalized
  if (firstSentence.length <= maxLength) {
    return firstSentence
  }
  return `${firstSentence.slice(0, maxLength)}…`
}

export function applyRiskRuleJsonState(target, payload, apiPayload) {
  const rulePayload = isPlainObject(payload) ? payload : {}
  const metadata = rulePayload.metadata && typeof rulePayload.metadata === 'object'
    ? rulePayload.metadata
    : {}
  const apiConfig = apiPayload?.config_json && typeof apiPayload.config_json === 'object'
    ? apiPayload.config_json
    : {}
  const fullDescription =
    resolveRiskRuleDescription(rulePayload) ||
    normalizeText(apiPayload?.description) ||
    normalizeText(target.riskRuleDescription)
  const riskCategory =
    normalizeText(metadata.expense_category_label) ||
    normalizeText(apiConfig.expense_category_label) ||
    normalizeText(rulePayload.risk_category) ||
    resolveRiskRuleCategory({ ...target, risk_category: rulePayload.risk_category, config_json: rulePayload })
  const businessStage = resolveRiskRuleBusinessStage(target, rulePayload)
  const riskRuleFields = resolveRiskRuleFields(rulePayload)
  const riskRuleCreatedAt = resolveRiskRuleCreatedAt(rulePayload, target.createdAt || target.updatedAt)
  const riskRuleScoreLevel = resolveRiskRuleScoreLevel(rulePayload, apiConfig)

  const statusValue = apiPayload?.status || target.statusValue || 'draft'
  const onlineMeta = resolveRiskRuleOnlineMeta(statusValue)
  const isEnabledValue = resolveRiskRuleEnabled(target, rulePayload)

  const publisher =
    target.creator ||
    normalizeText(apiPayload?.owner) ||
    normalizeText(metadata.created_by) ||
    normalizeText(apiPayload?.recent_versions?.[0]?.created_by) ||
    '未知'

  let publishedAt = target.publishedAt || '-'
  if (apiPayload?.recent_versions) {
    const history = buildHistory(apiPayload.recent_versions, { ...target, config_json: payload })
    const publishedVersionObj = history.find((item) => item.isPublished || item.lifecycleState === 'published')
    publishedAt = publishedVersionObj ? publishedVersionObj.time : (apiPayload?.latest_review?.reviewed_at ? formatDateTime(apiPayload.latest_review.reviewed_at) : '-')
  } else if (apiPayload?.latest_review?.reviewed_at) {
    publishedAt = formatDateTime(apiPayload.latest_review.reviewed_at)
  }

  return {
    ...target,
    riskRuleDescription: fullDescription,
    riskRuleBusinessDescription: resolveRiskRuleBusinessDescription(rulePayload, fullDescription),
    riskRuleSubtitle: buildRiskListSubtitle(fullDescription, 48),
    riskCategory,
    businessStageValue: businessStage.value,
    businessStageLabel: businessStage.label,
    scope: riskCategory,
    riskRuleSourceRef: resolveRiskRuleSourceRef(rulePayload),
    riskRuleSeverity: riskRuleScoreLevel || resolveRiskRuleSeverity(rulePayload),
    riskRuleSeverityLabel: riskRuleScoreLevel
      ? resolveRiskRuleScoreLabel(rulePayload, apiConfig)
      : resolveRiskRuleSeverityLabel(rulePayload),
    riskRuleScore: resolveRiskRuleScore(rulePayload, apiConfig),
    riskRuleScoreLabel: resolveRiskRuleScoreLabel(rulePayload, apiConfig),
    riskRuleScoreLevel: riskRuleScoreLevel || resolveRiskRuleSeverity(rulePayload),
    riskRuleScoreDetail: resolveRiskRuleScoreDetail(rulePayload, apiConfig),
    riskRuleCreatedAt: formatDateTime(riskRuleCreatedAt),
    riskRuleAgeLabel: formatRiskRuleAge(riskRuleCreatedAt),
    riskRuleFields,
    riskRuleFieldSummary: buildRiskRuleFieldSummary(riskRuleFields),
    riskRuleFlow: resolveRiskRuleFlow(rulePayload, riskRuleFields),
    riskRuleFlowDiagramSvg: resolveRiskRuleFlowDiagramSvg({
      ...rulePayload,
      flow_diagram_svg: normalizeText(apiPayload?.flow_diagram_svg) || rulePayload?.flow_diagram_svg
    }),
    riskRuleRequiresAttachment: Boolean(
      rulePayload.requires_attachment ||
      metadata.requires_attachment ||
      apiConfig.requires_attachment ||
      target.configJson?.requires_attachment
    ),
    riskRuleSummary: {
      name: apiPayload?.name || target.name,
      evaluator: apiPayload?.evaluator || rulePayload.evaluator || '',
      ontologySignal: apiPayload?.ontology_signal || rulePayload.ontology_signal || '',
      inputs: apiPayload?.inputs || rulePayload.inputs || {},
      outcomes: apiPayload?.outcomes || rulePayload.outcomes || {}
    },
    riskRuleJsonText: JSON.stringify(rulePayload, null, 2),
    isOnlineValue: onlineMeta.online,
    isOnlineLabel: onlineMeta.label,
    isOnlineTone: onlineMeta.tone,
    isEnabledValue,
    isEnabledLabel: isEnabledValue ? '是' : '否',
    isEnabledTone: isEnabledValue ? 'success' : 'disabled',
    lastOperationLabel: resolveLastOperationLabel(target, {
      actor: publisher,
      at: riskRuleCreatedAt
    }),
    publisher,
    publishedAt
  }
}

export function cloneJsonObject(value) {
  if (!isPlainObject(value)) {
    return null
  }
  try {
    return JSON.parse(JSON.stringify(value))
  } catch {
    return { ...value }
  }
}

export function resolveRuleTemplateLabel(value) {
  const templateKey = normalizeText(value)
  return RULE_TEMPLATE_LABELS[templateKey] || templateKey || '未指定模板'
}

export function extractRuntimeRuleFromMarkdown(markdown) {
  const match = String(markdown || '').match(EXPENSE_RULE_BLOCK_PATTERN)
  if (!match) {
    return null
  }

  try {
    const payload = JSON.parse(match[1])
    return isPlainObject(payload) ? payload : null
  } catch {
    return null
  }
}

export function extractSpreadsheetMetaFromMarkdown(markdown) {
  const match = String(markdown || '').match(RULE_SPREADSHEET_BLOCK_PATTERN)
  if (!match) {
    return null
  }

  try {
    const payload = JSON.parse(match[1])
    return isPlainObject(payload) ? payload : null
  } catch {
    return null
  }
}

export function stripRuntimeRuleBlock(markdown) {
  const text = String(markdown || '')
  const stripped = text.replace(EXPENSE_RULE_BLOCK_PATTERN, '').replace(/\n{3,}/g, '\n\n').trim()
  return stripped
}

export function stringifyRuntimeRule(runtimeRule) {
  return JSON.stringify(isPlainObject(runtimeRule) ? runtimeRule : {}, null, 2)
}

export function parseRuntimeRuleText(runtimeRuleText) {
  const text = normalizeText(runtimeRuleText)
  if (!text) {
    return null
  }

  try {
    const payload = JSON.parse(text)
    return isPlainObject(payload) ? payload : null
  } catch {
    return null
  }
}

export function buildDefaultRuntimeRule(source) {
  const configJson = readConfigJson(source)
  const scenarioItems = Array.isArray(source?.scenario_json)
    ? source.scenario_json
    : Array.isArray(source?.scenarioList)
      ? source.scenarioList
      : []
  const configRuntimeRule = cloneJsonObject(configJson.runtime_rule)

  return {
    kind: normalizeText(configRuntimeRule?.kind || configJson.runtime_kind) || 'policy_rule_draft',
    version:
      typeof configRuntimeRule?.version === 'number' && Number.isFinite(configRuntimeRule.version)
        ? configRuntimeRule.version
        : 1,
    template_key:
      normalizeText(configRuntimeRule?.template_key || configJson.rule_template_key) || 'general_policy_v1',
    rule_name: normalizeText(configRuntimeRule?.rule_name || source?.name) || '未命名规则',
    scenario:
      normalizeText(configRuntimeRule?.scenario || scenarioItems[0]) || 'expense',
    review_required:
      typeof configRuntimeRule?.review_required === 'boolean' ? configRuntimeRule.review_required : true
  }
}

export function resolveRuntimeRuleForVersion(source, rawMarkdown, runtimeRuleFallback = null) {
  return (
    cloneJsonObject(extractRuntimeRuleFromMarkdown(rawMarkdown)) ||
    cloneJsonObject(runtimeRuleFallback) ||
    buildDefaultRuntimeRule(source)
  )
}

export function buildMarkdownVersionContent(markdownContent, runtimeRule) {
  const body = stripRuntimeRuleBlock(markdownContent)
  const runtimeBlock = ['```expense-rule', stringifyRuntimeRule(runtimeRule), '```'].join('\n')
  return body ? `${body}\n\n${runtimeBlock}` : runtimeBlock
}

export function makeShort(value) {
  const text = normalizeText(value).replace(/\s+/g, '')
  if (!text) {
    return 'AG'
  }
  return text.slice(0, 2).toUpperCase()
}

export function formatDateTime(value) {
  if (!value) {
    return '未记录'
  }

  const date = new Date(value)
  if (Number.isNaN(date.getTime())) {
    return String(value)
  }

  return new Intl.DateTimeFormat('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false
  })
    .format(date)
    .replace(/\//g, '-')
}

export function resolveDomainLabel(value) {
  return DOMAIN_LABELS[value] || normalizeText(value) || '未分类'
}

export function resolveStatusMeta(value) {
  return STATUS_META[value] || { label: normalizeText(value) || '未知状态', tone: 'draft' }
}

export function resolveReviewMeta(value) {
  return REVIEW_META[value] || { label: '暂无审核', tone: 'draft' }
}

export function resolveTimelineEventMeta(value) {
  return {
    created: { label: '创建工作稿', icon: 'mdi mdi-file-document-edit-outline', tone: 'draft' },
    submitted: { label: '提交审核', icon: 'mdi mdi-send-outline', tone: 'warning' },
    approved: { label: '审核通过', icon: 'mdi mdi-check-decagram-outline', tone: 'success' },
    rejected: { label: '审核驳回', icon: 'mdi mdi-close-octagon-outline', tone: 'danger' },
    published: { label: '正式上线', icon: 'mdi mdi-rocket-launch-outline', tone: 'success' },
    restored: { label: '恢复生成工作稿', icon: 'mdi mdi-history', tone: 'info' }
  }[normalizeText(value)] || { label: normalizeText(value) || '版本事件', icon: 'mdi mdi-circle-medium', tone: 'draft' }
}

export function resolveDiffChangeMeta(value) {
  return {
    added: { label: '新增', tone: 'success' },
    removed: { label: '删除', tone: 'danger' },
    modified: { label: '修改', tone: 'warning' }
  }[normalizeText(value)] || { label: normalizeText(value) || '变化', tone: 'draft' }
}

export function formatScenarioList(items) {
  if (!Array.isArray(items) || !items.length) {
    return '未配置场景'
  }

  return items
    .map((item) => SCENARIO_LABELS[item] || item)
    .filter(Boolean)
    .join(' / ')
}

export function buildHistory(recentVersions = [], source) {
  const currentRuntimeRule = cloneJsonObject(readConfigJson(source).runtime_rule)

  return recentVersions.map((item) => {
    const rawContent = typeof item.content === 'string' ? item.content : ''
    return {
      version: item.version,
      note: item.change_note || '无版本说明',
      time: formatDateTime(item.created_at),
      content: rawContent,
      markdownContent: stripRuntimeRuleBlock(rawContent),
      runtimeRule: resolveRuntimeRuleForVersion(
        source,
        rawContent,
        item.is_current ? currentRuntimeRule : null
      ),
      spreadsheetMeta: extractSpreadsheetMetaFromMarkdown(rawContent),
      contentType: item.content_type,
      createdBy: item.created_by,
      isCurrent: Boolean(item.is_current),
      isPublished: Boolean(item.is_published),
      isWorking: Boolean(item.is_working),
      lifecycleState: item.lifecycle_state || 'history',
      lifecycleMeta: VERSION_STATE_META[item.lifecycle_state] || VERSION_STATE_META.history
    }
  })
}

export function resolveTypeKey(assetType) {
  if (assetType === 'rule') {
    return 'rules'
  }
  if (assetType === 'mcp') {
    return 'mcp'
  }
  return ''
}

export function formatSeverity(value) {
  const severity = normalizeText(value).toLowerCase()
  if (severity === 'high') {
    return '高风险'
  }
  if (severity === 'medium') {
    return '中风险'
  }
  if (severity === 'low') {
    return '低风险'
  }
  return '未配置'
}

export function formatInputSummary(items) {
  if (!Array.isArray(items) || !items.length) {
    return '无输入'
  }
  return `${items.length} 项输入`
}

export function formatOutputSummary(items) {
  if (!Array.isArray(items) || !items.length) {
    return '无输出'
  }
  return `${items.length} 项输出`
}

export function findLatestMcpCall(runs, assetCode) {
  const expectedToolName = normalizeText(assetCode).replace(/^mcp\./, '')

  for (const run of runs) {
    for (const toolCall of run.tool_calls || []) {
      const toolName = normalizeText(toolCall.tool_name)
      if (
        toolName === expectedToolName ||
        toolName.endsWith(expectedToolName) ||
        expectedToolName.endsWith(toolName)
      ) {
        return {
          run,
          toolCall
        }
      }
    }
  }

  return null
}

export function buildRowRuntime(asset, typeKey) {
  if (typeKey === 'rules') {
    return formatSeverity(asset.config_json?.severity)
  }
  if (typeKey === 'mcp') {
    return normalizeText(asset.config_json?.endpoint) || '未配置地址'
  }
  return ''
}

export function buildRowMetric(asset, typeKey) {
  if (typeKey === 'rules') {
    return normalizeText(asset.modified_by) || '未记录'
  }
  if (typeKey === 'mcp') {
    return asset.config_json?.timeout_ms ? `${asset.config_json.timeout_ms} ms` : '未配置超时'
  }
  return ''
}

export function formatSpreadsheetChangeSummary(summary) {
  const normalized = normalizeText(summary)
  return (
    normalized
      .replace(/^(ONLYOFFICE\s*)?在线编辑[：:]\s*/i, '')
      .replace(/^ONLYOFFICE\s*在线编辑保存[。.]?\s*/i, '')
      .replace(/^保存表格[：:]\s*/i, '')
      .trim() || '表格内容已保存。'
  )
}

export function buildListItem(asset) {
  const typeKey = resolveTypeKey(asset.asset_type)
  const tabId = resolveTabId(asset, typeKey)
  if (!tabId) {
    return null
  }

  const tabMeta = resolveTabMeta(tabId, typeKey)
  const statusMeta = resolveStatusMeta(asset.status)
  const workingVersion = asset.working_version || asset.current_version || '-'
  const changeCount =
    typeof asset.change_count === 'number'
      ? asset.change_count
      : Array.isArray(asset.recent_versions)
        ? Math.max(asset.recent_versions.length - 1, 0)
        : 0
  const modifiedBy =
    normalizeText(asset.modified_by) ||
    normalizeText(
      Array.isArray(asset.recent_versions)
        ? asset.recent_versions.find((item) => item.version === workingVersion)?.created_by
        : ''
    )
  const isRiskRule = tabId === 'riskRules'
  const usesSpreadsheetRule = typeKey === 'rules' && isSpreadsheetRuleSource(asset)
  const usesJsonRiskRule = typeKey === 'rules' && isJsonRiskRuleSource(asset)
  const ruleDocument = readRuleDocumentMeta(asset)
  const ruleScenarioCategory = typeKey === 'rules' ? resolveRuleScenarioCategory(asset, tabId) : ''
  const listSubtitle = isRiskRule
    ? buildRiskListSubtitle(asset.description)
    : normalizeText(asset.description)
  const onlineMeta = resolveRiskRuleOnlineMeta(asset.status)
  const isOnlineValue = onlineMeta.online
  const isEnabledValue = usesJsonRiskRule ? resolveRiskRuleEnabled(asset) : true
  const reviewer = normalizeText(asset.reviewer) || '待分配'
  const creator =
    normalizeText(asset.owner) ||
    normalizeText(asset.config_json?.generation_request?.actor) ||
    modifiedBy ||
    '未知'
  const publisher = isRiskRule ? creator : ''
  const riskRuleCreatedAt = formatDateTime(asset.created_at || asset.updated_at)
  const businessStage = usesJsonRiskRule
    ? resolveRiskRuleBusinessStage(asset)
    : { value: '', label: '' }
  const ruleScenarioList = typeKey === 'rules' ? resolveRuleScenarioList(asset, tabId) : []
  const riskScoreLevel = usesJsonRiskRule
    ? resolveRiskRuleScoreLevel(asset.config_json, asset.config_json)
    : ''
  const riskLevelValue = usesJsonRiskRule
    ? riskScoreLevel || resolveRiskRuleSeverity(asset.config_json)
    : ''
  const riskLevelLabel = usesJsonRiskRule
    ? riskScoreLevel
      ? resolveRiskRuleScoreLabel(asset.config_json, asset.config_json) || resolveRiskRuleSeverityLabel(asset.config_json)
      : resolveRiskRuleSeverityLabel(asset.config_json)
    : ''
  const displayName = asset.name
  const displayCode = asset.code
  const displaySummary = listSubtitle
  const displayOwner = isRiskRule ? creator : asset.owner
  const displayReviewer = reviewer
  const displayCategory = resolveDomainLabel(asset.domain)
  const displayScope = typeKey === 'rules' ? ruleScenarioCategory || '通用' : formatScenarioList(asset.scenario_json)
  const displayEnabledValue = isEnabledValue
  const displayEnabledLabel = isEnabledValue ? '是' : '否'
  const displayEnabledTone = isEnabledValue ? 'success' : 'disabled'

  return {
    id: asset.id,
    tabId,
    type: typeKey,
    isPreviewMock: Boolean(asset.isPreviewMock),
    usesSpreadsheetRule,
    usesJsonRiskRule,
    ruleDocument,
    typeLabel: tabMeta.typeLabel,
    short: makeShort(displayName),
    name: displayName,
    code: displayCode,
    rawCode: asset.code,
    summary: displaySummary,
    listSubtitle: displaySummary,
    category: displayCategory,
    owner: displayOwner,
    reviewer: displayReviewer,
    scope: displayScope,
    riskCategory: ruleScenarioCategory,
    scenarioList: ruleScenarioList,
    businessStageValue: businessStage.value,
    businessStageLabel: businessStage.label,
    riskLevelValue,
    riskLevelLabel,
    riskLevelTone: riskLevelValue,
    model: buildRowRuntime(asset, typeKey),
    version: workingVersion,
    versionDisplay: typeKey === 'rules' ? `${changeCount} 次` : workingVersion,
    publishedVersion: asset.published_version || '-',
    workingVersion,
    status: statusMeta.label,
    statusValue: asset.status,
    statusTone: statusMeta.tone,
    hitRate: isRiskRule ? publisher : buildRowMetric({ ...asset, modified_by: modifiedBy }, typeKey),
    creator,
    publisher,
    publishedAt: isOnlineValue ? formatDateTime(asset.published_at || asset.updated_at) : '-',
    isOnlineValue,
    isOnlineLabel: onlineMeta.label,
    isOnlineTone: onlineMeta.tone,
    isEnabledValue: displayEnabledValue,
    isEnabledLabel: displayEnabledLabel,
    isEnabledTone: displayEnabledTone,
    modifiedBy,
    changeCount,
    updatedAt: isRiskRule ? riskRuleCreatedAt : formatDateTime(asset.updated_at),
    badgeTone: tabMeta.badgeTone,
    domainValue: asset.domain
  }
}

export function buildRuleFields(detail) {
  const ruleDocument = readRuleDocumentMeta(detail)
  const ruleScenarioCategory = resolveRuleScenarioCategory(detail)
  return [
    { label: '规则编码', value: detail.code },
    {
      label: '明细载体',
      value: isSpreadsheetRuleSource(detail) ? 'Excel 表格' : 'Markdown / JSON'
    },
    ...(ruleDocument
      ? [
          {
            label: '关联文件',
            value: normalizeText(ruleDocument.file_name) || '未上传'
          }
        ]
      : []),
    {
      label: '模板键',
      value: normalizeText(detail.config_json?.rule_template_key) || '未指定'
    },
    { label: '业务域', value: resolveDomainLabel(detail.domain) },
    {
      label: '运行时类型',
      value: normalizeText(detail.config_json?.runtime_kind) || 'policy_rule_draft'
    },
    { label: '适用场景', value: ruleScenarioCategory || '通用' },
    { label: '线上版本', value: detail.published_version || '-' },
    { label: '工作版本', value: detail.working_version || detail.current_version || '-' }
  ]
}

export function buildMcpFields(detail, latestCall) {
  const content = detail.current_version_content || {}
  return [
    { label: '服务编码', value: detail.code },
    { label: '调用地址', value: normalizeText(detail.config_json?.endpoint) || '未配置' },
    { label: '鉴权方式', value: normalizeText(content.auth_mode) || '未配置' },
    {
      label: '最近调用',
      value: latestCall ? `${latestCall.toolCall.status} / ${formatDateTime(latestCall.run.started_at)}` : '暂无调用记录'
    }
  ]
}

export function buildFields(detail, typeKey, latestCall) {
  if (typeKey === 'rules') {
    return buildRuleFields(detail)
  }
  if (typeKey === 'mcp') {
    return buildMcpFields(detail, latestCall)
  }
  return []
}

export function buildPromptSections(detail, typeKey) {
  const content = detail.current_version_content || {}

  if (typeKey === 'mcp') {
    return [
      {
        title: '服务类型',
        intent: '协议说明',
        content: normalizeText(content.service_type) || '未配置服务类型。'
      },
      {
        title: '鉴权方式',
        intent: '安全要求',
        content: normalizeText(content.auth_mode) || '未配置鉴权方式。'
      },
      {
        title: '降级策略',
        intent: '失败处理',
        content: normalizeText(content.degrade_strategy) || '未配置降级策略。'
      }
    ]
  }

  return []
}

export function buildOutputRules(detail, typeKey) {
  const content = detail.current_version_content || {}

  if (typeKey === 'rules') {
    if (isSpreadsheetRuleSource(detail)) {
      return [
        '规则详情页以内联 Excel 表格作为主载体，管理员可直接编辑当前版本。',
        '上传新的 Excel 文件后，会自动生成新的规则版本快照。',
        '切换到历史版本时仅支持预览，不允许直接覆盖历史版本。',
        '规则表发生变更后，仍需完成审核才能再次正式上线。'
      ]
    }
    return [
      '规则使用固定模板落 Markdown，并配套维护 runtime_rule JSON。',
      '保存 Markdown 或 JSON 都会生成新版本快照。',
      '未审核通过的规则版本不能正式上线。',
      '版本切换当前只影响前端展示内容，不会直接回滚后端版本。'
    ]
  }

  if (typeKey === 'mcp') {
    return [
      `服务地址：${normalizeText(detail.config_json?.endpoint) || '未配置'}`,
      `鉴权方式：${normalizeText(content.auth_mode) || '未配置'}`,
      `降级策略：${normalizeText(content.degrade_strategy) || '未配置'}`
    ]
  }

  return []
}

export function buildTests(detail, typeKey, latestCall) {
  if (typeKey === 'rules') {
    const reviewMeta = resolveReviewMeta(detail.latest_review?.review_status)
    return [
      {
        name: '审核状态',
        input: detail.latest_review?.version || detail.current_version || '暂无版本',
        result: reviewMeta.label,
        tone: reviewMeta.tone
      },
      {
        name: '上线状态',
        input: detail.current_version || '暂无版本',
        result: resolveStatusMeta(detail.status).label,
        tone: resolveStatusMeta(detail.status).tone
      }
    ]
  }

  if (typeKey === 'mcp') {
    return [
      {
        name: '最近调用状态',
        input: latestCall?.run?.run_id || '暂无调用',
        result: latestCall?.toolCall?.status || '未记录',
        tone: latestCall?.toolCall?.status === 'failed' ? 'danger' : 'success'
      },
      {
        name: '最近调用耗时',
        input: latestCall?.toolCall?.tool_name || '暂无调用',
        result:
          typeof latestCall?.toolCall?.duration_ms === 'number'
            ? `${latestCall.toolCall.duration_ms} ms`
            : '未记录',
        tone: 'success'
      }
    ]
  }

  return []
}

export function buildTools(detail, typeKey, latestCall) {
  const content = detail.current_version_content || {}

  if (typeKey === 'mcp') {
    return [
      {
        name: normalizeText(content.service_type) || '未配置服务类型',
        scope: '服务类型',
        mode: 'MCP',
        tone: 'active'
      },
      {
        name: normalizeText(content.auth_mode) || '未配置鉴权方式',
        scope: '鉴权',
        mode: '安全',
        tone: 'safe'
      },
      {
        name: latestCall?.run?.run_id || '暂无调用记录',
        scope: '最近 Run',
        mode: latestCall?.toolCall?.status || '未执行',
        tone: latestCall?.toolCall?.status === 'failed' ? 'danger' : 'active'
      }
    ]
  }

  return []
}

export function buildPublishDescription(detail, typeKey) {
  if (typeKey === 'rules') {
    if (detail.published_version && detail.working_version && detail.published_version !== detail.working_version) {
      return '当前存在尚未上线的工作版本，系统运行仍以线上版本为准。'
    }
    if (detail.status === 'active') {
      return '当前规则线上版本已生效，仍可继续保存新的工作版本并重新走审核。'
    }
    return '当前规则需要先完成审核，再调用上线接口正式激活。'
  }

  return DETAIL_TITLES[typeKey]?.publishDesc || ''
}

export function buildDetailViewModel(detail, runs) {
  const typeKey = resolveTypeKey(detail.asset_type)
  const tabId = resolveTabId(detail, typeKey) || typeKey
  if (!typeKey || !tabId) {
    return null
  }
  const tabMeta = resolveTabMeta(tabId, typeKey)
  const latestCall = typeKey === 'mcp' ? findLatestMcpCall(runs, detail.code) : null
  const configJson = readConfigJson(detail)
  const statusMeta = resolveStatusMeta(detail.status)
  const reviewMeta = resolveReviewMeta(detail.latest_review?.review_status)
  const history = buildHistory(detail.recent_versions || [], detail)
  const previewVersion = history.find((item) => item.isWorking) || history[0] || null
  const usesSpreadsheetRule = typeKey === 'rules' && isSpreadsheetRuleSource(detail)
  const usesJsonRiskRule = typeKey === 'rules' && isJsonRiskRuleSource(detail)
  const ruleDocument = readRuleDocumentMeta(detail)
  const previewRawMarkdown =
    detail.current_version_content_type === 'markdown'
      ? String(previewVersion?.content ?? detail.current_version_content ?? '')
      : ''
  const previewRuntimeRule = resolveRuntimeRuleForVersion(
    detail,
    previewRawMarkdown,
    previewVersion?.runtimeRule || configJson.runtime_rule
  )
  const previewMarkdown = stripRuntimeRuleBlock(previewRawMarkdown)
  const titles = DETAIL_TITLES[typeKey]
  const previewChangeNote = previewVersion?.note || detail.current_version_change_note || '无版本说明'
  const ruleTemplateKey = normalizeText(configJson.rule_template_key || previewRuntimeRule.template_key)
  const ruleTemplateLabel = normalizeText(configJson.rule_template_label) || resolveRuleTemplateLabel(ruleTemplateKey)
  const runtimeKind = normalizeText(configJson.runtime_kind || previewRuntimeRule.kind) || 'policy_rule_draft'
  const ruleScenarioCategory = typeKey === 'rules' ? resolveRuleScenarioCategory(detail, tabId) : ''
  const ruleScenarioList = typeKey === 'rules' ? resolveRuleScenarioList(detail, tabId) : []
  const isEnabledValue = usesJsonRiskRule ? resolveRiskRuleEnabled(detail) : true
  const generationStatus = normalizeText(configJson.generation_status || detail.status)
  const riskRuleGenerationFailed = usesJsonRiskRule && (detail.status === 'failed' || generationStatus === 'failed')
  const riskRuleGenerationBusy = usesJsonRiskRule && (detail.status === 'generating' || generationStatus === 'generating')
  const riskRuleCreator =
    normalizeText(detail.owner) ||
    normalizeText(detail.recent_versions?.[0]?.created_by) ||
    '未知'
  const onlineMeta = resolveRiskRuleOnlineMeta(detail.status)
  const businessStage = usesJsonRiskRule
    ? resolveRiskRuleBusinessStage(detail)
    : { value: '', label: '' }

  const initialRiskRuleScore = resolveRiskRuleScore(configJson, configJson)
  const initialRiskRuleScoreLevel = resolveRiskRuleScoreLevel(configJson, configJson)
  const initialRiskRuleSeverity = initialRiskRuleScoreLevel || resolveRiskRuleSeverity(configJson)
  const detailName = detail.name
  const detailCode = detail.code
  const detailSummary = usesJsonRiskRule ? buildRiskListSubtitle(detail.description) : detail.description
  const detailOwner = detail.owner
  const detailReviewer = detail.reviewer || detail.latest_review?.reviewer || '待分配'
  const detailCategory = resolveDomainLabel(detail.domain)
  const detailScope = typeKey === 'rules' ? ruleScenarioCategory || '通用' : formatScenarioList(detail.scenario_json)
  const detailEnabledValue = isEnabledValue
  const detailEnabledLabel = isEnabledValue ? '是' : '否'
  const detailEnabledTone = isEnabledValue ? 'success' : 'disabled'

  return {
    id: detail.id,
    tabId,
    type: typeKey,
    typeLabel: tabMeta.typeLabel,
    short: makeShort(detailName),
    name: detailName,
    code: detailCode,
    rawCode: detail.code,
    summary: detailSummary,
    listSubtitle: normalizeText(detailSummary),
    owner: detailOwner,
    reviewer: detailReviewer,
    category: detailCategory,
    scope: detailScope,
    businessStageValue: businessStage.value,
    businessStageLabel: businessStage.label,
    version: detail.working_version || detail.current_version || '-',
    currentVersion: detail.current_version || '-',
    publishedVersion: detail.published_version || '-',
    workingVersion: detail.working_version || detail.current_version || '-',
    displayVersion: previewVersion?.version || detail.working_version || detail.current_version || '-',
    status: statusMeta.label,
    statusValue: detail.status,
    statusTone: statusMeta.tone,
    hitRate: buildRowMetric(detail, typeKey),
    createdAt: detail.created_at,
    updatedAt: formatDateTime(detail.updated_at),
    badgeTone: tabMeta.badgeTone,
    configJson,
    usesSpreadsheetRule,
    usesJsonRiskRule,
    riskRuleJsonText: '{}',
    riskRuleSummary: null,
    riskRuleDescription: '',
    riskRuleBusinessDescription: '',
    riskRuleSubtitle: usesJsonRiskRule ? buildRiskListSubtitle(detail.description) : '',
    riskRuleSourceRef: '',
    riskRuleSeverity: initialRiskRuleSeverity,
    riskRuleScore: initialRiskRuleScore,
    riskRuleScoreLevel: initialRiskRuleScoreLevel || initialRiskRuleSeverity,
    riskRuleScoreDetail: resolveRiskRuleScoreDetail(configJson, configJson),
    riskRuleSeverityLabel: initialRiskRuleScoreLevel
      ? resolveRiskRuleScoreLabel(configJson, configJson)
      : resolveRiskRuleSeverityLabel(configJson),
    riskRuleScoreLabel: resolveRiskRuleScoreLabel(configJson, configJson),
    riskRuleCreatedAt: formatDateTime(detail.created_at),
    riskRuleAgeLabel: formatRiskRuleAge(detail.created_at),
    isOnlineValue: onlineMeta.online,
    isOnlineLabel: onlineMeta.label,
    isOnlineTone: onlineMeta.tone,
    isEnabledValue: detailEnabledValue,
    isEnabledLabel: detailEnabledLabel,
    isEnabledTone: detailEnabledTone,
    publisher:
      detail.status === 'active'
        ? normalizeText(detail.published_by) ||
          detail.latest_review?.reviewer ||
          detail.reviewer ||
          (detail.recent_versions && detail.recent_versions[0]?.created_by) ||
          '系统管理员'
        : riskRuleCreator,
    creator: riskRuleCreator,
    publishedAt:
      history.find((item) => item.isPublished || item.lifecycleState === 'published')?.time ||
      (detail.published_at ? formatDateTime(detail.published_at) : '') ||
      (detail.latest_review?.reviewed_at ? formatDateTime(detail.latest_review.reviewed_at) : '-'),
    lastOperationLabel: resolveLastOperationLabel(detail, {
      actor: riskRuleCreator,
      at: detail.created_at
    }),
    lastOperationTone: onlineMeta.tone,
    riskRuleFields: [],
    riskRuleFieldSummary: '未识别字段',
    riskRuleFlow: resolveRiskRuleFlow({}, []),
    riskRuleFlowDiagramSvg: normalizeText(configJson.flow_diagram_svg),
    riskRuleRequiresAttachment: Boolean(configJson.requires_attachment),
    riskRuleGenerationStatus: generationStatus,
    riskRuleGenerationFailed,
    riskRuleGenerationBusy,
    riskRuleGenerationError: normalizeText(configJson.generation_error),
    latestTestSummary: detail.latest_test_summary || detail.latestTestSummary || null,
    riskCategory: typeKey === 'rules' ? ruleScenarioCategory : '',
    ruleDocument,
    scenarioList: typeKey === 'rules' && ruleScenarioList.length
      ? ruleScenarioList
      : Array.isArray(detail.scenario_json)
        ? [...detail.scenario_json]
        : [],
    markdownContent: previewMarkdown,
    runtimeRuleText: stringifyRuntimeRule(previewRuntimeRule),
    ruleTemplateKey,
    ruleTemplateLabel,
    runtimeKind,
    currentVersionContentType: detail.current_version_content_type,
    currentVersionChangeNote: detail.current_version_change_note || '无版本说明',
    displayVersionChangeNote: previewChangeNote,
    reviewStatusLabel: reviewMeta.label,
    reviewStatusTone: reviewMeta.tone,
    reviewStatusValue: detail.latest_review?.review_status || '',
    reviewTimeLabel: formatDateTime(detail.latest_review?.reviewed_at),
    reviewNote: detail.latest_review?.review_note || '',
    latestCall,
    fields: buildFields(detail, typeKey, latestCall),
    promptSections:
      typeKey === 'rules' ? [] : buildPromptSections(detail, typeKey),
    outputRules: buildOutputRules(detail, typeKey),
    tests: buildTests(detail, typeKey, latestCall),
    triggers:
      typeKey === 'rules'
        ? [ruleScenarioCategory || '通用']
        : detail.scenario_json?.length
          ? detail.scenario_json.map((item) => SCENARIO_LABELS[item] || item)
          : ['未配置场景'],
    tools:
      typeKey === 'rules'
        ? [
            {
              name: detail.latest_review?.reviewer || '待分配审核人',
              scope: '审核负责人',
              mode: reviewMeta.label,
              tone: reviewMeta.tone
            },
            {
              name: detail.published_version || '暂无版本',
              scope: '线上版本',
              mode: '正式生效',
              tone: 'safe'
            },
            {
              name: detail.working_version || detail.current_version || '暂无版本',
              scope: '工作版本',
              mode: detail.current_version_change_note || '无版本说明',
              tone: 'safe'
            }
          ]
        : buildTools(detail, typeKey, latestCall),
    history,
    configTitle: titles.configTitle,
    configDesc: titles.configDesc,
    detailTitle: titles.detailTitle,
    detailDesc: titles.detailDesc,
    outputTitle: titles.outputTitle,
    outputDesc: titles.outputDesc,
    ruleListTitle: titles.ruleListTitle,
    checkListTitle: titles.checkListTitle,
    triggerTitle: titles.triggerTitle,
    triggerDesc: titles.triggerDesc,
    toolTitle: titles.toolTitle,
    toolDesc: titles.toolDesc,
    historyTitle: titles.historyTitle,
    historyDesc: titles.historyDesc,
    publishTitle: titles.publishTitle,
    publishDesc: buildPublishDescription(detail, typeKey),
    publishMeta:
      typeKey === 'rules'
        ? `最近保存：${formatDateTime(detail.updated_at)}`
        : `最近更新：${formatDateTime(detail.updated_at)}`,
    publishState: statusMeta.label,
    latestReviewVersion: detail.latest_review?.version || detail.current_version || '-',
    loading: false
  }
}
