import assert from 'node:assert/strict'
import test from 'node:test'

import {
  buildAnalyticsWindow,
  buildCommercialMutation,
  buildCommercialValueLedgers,
  buildPricingScenarioPayload,
  buildPricingScenarioView,
  buildQuotaRows,
  classifyCommercialAccountState,
  createDefaultPricingScenarioForm,
  formatCurrencyAmount,
  formatMetricValues,
  formatRatio
} from '../src/components/commercial/commercialWorkspaceModel.js'

test('客户价值账和平台经营账保持严格分离', () => {
  const ledgers = buildCommercialValueLedgers({
    verifiedCashSavings: metric('verified_cash_savings', '客户财务确认现金节省', [{ currency: 'CNY', amount: '100' }]),
    customerCharges: metric('customer_charges', '客户合同收费', [{ currency: 'CNY', amount: '40' }], 'partial'),
    customerRoi: { ...metric('customer_roi', '客户现金 ROI', [], 'partial'), ratios: [{ currency: 'CNY', ratio: '1.5', numerator: '60', denominator: '40' }] },
    customerLaborValue: unavailable('customer_labor_value', '客户认可工时价值'),
    internalCosts: metric('internal_costs', '平台内部成本', [{ currency: 'CNY', amount: '12' }]),
    contributionMargin: metric('contribution_margin', '贡献毛利', [{ currency: 'CNY', amount: '28' }], 'partial')
  })

  assert.deepEqual(ledgers.customer.map((item) => item.category), [
    'verifiedCashSavings', 'customerCharges', 'customerRoi', 'customerLaborValue'
  ])
  assert.deepEqual(ledgers.platform.map((item) => item.category), ['internalCosts', 'contributionMargin'])
  assert.equal(ledgers.customer[2].displayValue, '150.0%')
  assert.equal(ledgers.platform[0].displayValue, formatCurrencyAmount('12', 'CNY'))
})

test('不同币种逐行保留，绝不在前端求和或换算', () => {
  const values = formatMetricValues(metric('verified', '已核验', [
    { currency: 'CNY', amount: '100' },
    { currency: 'USD', amount: '20' }
  ]))
  assert.deepEqual(values.map((item) => item.currency), ['CNY', 'USD'])
  assert.deepEqual(values.map((item) => item.amount), [100, 20])
  assert.match(values[0].displayValue, /100/u)
  assert.match(values[1].displayValue, /20/u)
  assert.equal(formatRatio('0.25'), '25.0%')
})

test('没有真实财务证据时明确不可用，不把未知伪装成零', () => {
  const [cash, labor] = buildCommercialValueLedgers({
    verifiedCashSavings: unavailable('verified_cash_savings', '客户财务确认现金节省'),
    customerCharges: unavailable('customer_charges', '客户合同收费'),
    customerRoi: unavailable('customer_roi', '客户现金 ROI'),
    customerLaborValue: unavailable('customer_labor_value', '客户认可工时价值'),
    internalCosts: unavailable('internal_costs', '平台内部成本'),
    contributionMargin: unavailable('contribution_margin', '贡献毛利')
  }).customer

  assert.equal(cash.status, 'unavailable')
  assert.equal(cash.displayValue, '不可用')
  assert.deepEqual(cash.values, [])
  assert.equal(labor.displayValue, '不可用')
  assert.deepEqual(labor.requiredInputs, ['真实财务证据'])
})

test('账户状态完整区分加载、权限、错误、未配置、部分与就绪', () => {
  assert.equal(classifyCommercialAccountState({ loading: true }), 'loading')
  assert.equal(classifyCommercialAccountState({ error: { status: 403 } }), 'permission')
  assert.equal(classifyCommercialAccountState({ error: new Error('offline') }), 'error')
  assert.equal(classifyCommercialAccountState({ account: { dataStatus: 'unavailable' } }), 'unavailable')
  assert.equal(classifyCommercialAccountState({ account: { dataStatus: 'partial' } }), 'partial')
  assert.equal(classifyCommercialAccountState({ account: { dataStatus: 'available' } }), 'ready')
})

test('权益列表保持失败关闭语义并区分不限量和未知硬上限', () => {
  const rows = buildQuotaRows({ quotas: [
    { status: 'unlimited', usedQuantity: '12', hardLimitRemaining: null, commerciallyAllowed: true, entitlement: { id: 'e1', entitlementKey: 'ocr', metricKey: 'ocr_calls', unit: '次', entitlementType: 'unlimited', version: 1 } },
    { status: 'inactive', usedQuantity: '0', hardLimitRemaining: null, commerciallyAllowed: false, reason: '订阅暂停', entitlement: { id: 'e2', entitlementKey: 'ai', metricKey: 'ai_calls', unit: '次', entitlementType: 'metered', version: 2 } }
  ] })
  assert.equal(rows[0].remainingLabel, '不限量')
  assert.equal(rows[1].remainingLabel, '未配置硬上限')
  assert.equal(rows[1].commerciallyAllowed, false)
  assert.equal(rows[1].reason, '订阅暂停')
})

test('分析窗口生成显式时区并拒绝倒序和超过两年', () => {
  const window = buildAnalyticsWindow({ start: '2026-04-01', end: '2026-07-01' })
  assert.match(window.start, /(Z|[+-]\d{2}:\d{2})$/u)
  assert.match(window.end, /(Z|[+-]\d{2}:\d{2})$/u)
  assert.throws(() => buildAnalyticsWindow({ start: '2026-07-01', end: '2026-04-01' }), /早于/u)
  assert.throws(() => buildAnalyticsWindow({ start: '2023-01-01', end: '2026-07-01' }), /731/u)
})

test('定价场景将百分比和时间窗转换为后端精确契约并执行前置校验', () => {
  const defaults = createDefaultPricingScenarioForm(
    { start: '2026-04-01', end: '2026-07-01' },
    new Date('2026-07-02T08:30:00Z')
  )
  assert.equal(defaults.targetContributionMarginPercent, '65')
  assert.equal(defaults.maxVerifiedSavingsSharePercent, '25')

  const payload = buildPricingScenarioPayload({
    ...defaults,
    asOf: '2026-07-02T08:30:00Z',
    targetContributionMarginPercent: '65',
    maxVerifiedSavingsSharePercent: '25'
  })
  assert.match(payload.start, /(Z|[+-]\d{2}:\d{2})$/u)
  assert.match(payload.end, /(Z|[+-]\d{2}:\d{2})$/u)
  assert.equal(payload.asOf, '2026-07-02T08:30:00.000Z')
  assert.equal(payload.targetContributionMarginRate, '0.65')
  assert.equal(payload.maxVerifiedSavingsShare, '0.25')

  assert.throws(() => buildPricingScenarioPayload({
    ...defaults, targetContributionMarginPercent: '95'
  }), /小于 95/u)
  assert.throws(() => buildPricingScenarioPayload({
    ...defaults, targetContributionMarginPercent: '94.999999'
  }), /小于 95/u)
  assert.equal(buildPricingScenarioPayload({
    ...defaults, targetContributionMarginPercent: '94.9999'
  }).targetContributionMarginRate, '0.949999')
  assert.throws(() => buildPricingScenarioPayload({
    ...defaults, maxVerifiedSavingsSharePercent: '0'
  }), /大于 0/u)
  assert.throws(() => buildPricingScenarioPayload({
    ...defaults, asOf: '2026-03-01T00:00:00Z'
  }), /不能早于/u)
})

test('定价结果逐币种展示价格走廊且绝不把缺失事实显示为零', () => {
  const view = buildPricingScenarioView({
    tenantId: 'tenant-a',
    start: '2026-04-01T00:00:00Z',
    end: '2026-07-01T23:59:59Z',
    asOf: '2026-07-02T00:00:00Z',
    targetContributionMarginRate: '0.65',
    maxVerifiedSavingsShare: '0.25',
    recommendedModel: 'hybrid',
    evidenceStatus: 'partial',
    scenarios: [
      {
        currency: 'CNY', status: 'feasible', internalCost: '20', verifiedCashSavings: '200',
        minimumSustainableCharge: '57.1429', maximumValueAlignedCharge: '50',
        maximumSuccessFee: '0', customerRoiAtMinimumCharge: '2.5',
        contributionMarginAtValueCeiling: '0.6', reason: '独立 CNY 走廊'
      },
      {
        currency: 'USD', status: 'cost_only', internalCost: '5', verifiedCashSavings: null,
        minimumSustainableCharge: '14.2857', maximumValueAlignedCharge: null,
        maximumSuccessFee: null, customerRoiAtMinimumCharge: null,
        contributionMarginAtValueCeiling: null, reason: '缺少 USD 价值证据'
      }
    ],
    notes: ['结果不会自动写套餐']
  })

  assert.equal(view.recommendedModelLabel, '混合定价（基础订阅 + 封顶成功费）')
  assert.equal(view.multiCurrency, true)
  assert.deepEqual(view.rows.map((row) => row.currency), ['CNY', 'USD'])
  assert.match(view.rows[0].minimumSustainableChargeLabel, /57/u)
  assert.equal(view.rows[1].maximumValueAlignedChargeLabel, '不可用')
  assert.equal(view.rows[1].maximumSuccessFeeLabel, '不可用')
  assert.equal(view.rows[1].customerRoiLabel, '—')
  assert.deepEqual(view.rows[1].missingInputs, ['同币种财务确认现金节省'])
  assert.doesNotMatch(view.rows[1].maximumValueAlignedChargeLabel, /0/u)
})

test('完全无证据的定价响应保留 unavailable 与缺失说明', () => {
  const view = buildPricingScenarioView({
    recommendedModel: 'pilot_collecting', evidenceStatus: 'unavailable', scenarios: [], notes: []
  })
  assert.equal(view.evidenceStatus, 'unavailable')
  assert.equal(view.evidenceStatusLabel, '不可用')
  assert.equal(view.recommendedModelLabel, '试点采集证据')
  assert.deepEqual(view.rows, [])
})

test('套餐、订阅与权益表单构造服务端事实时间和版本化载荷', () => {
  const plan = buildCommercialMutation('createPlan', {
    planCode: 'enterprise', name: '企业版', pricingModel: 'hybrid', billingInterval: 'annual',
    currency: 'cny', baseFee: '1000', includedSeats: 20, overageEnabled: true,
    effectiveFrom: '2026-07-16T09:30', effectiveTo: '', contractTermsJson: '{"approved_by":"cfo"}'
  })
  assert.equal(plan.currency, 'CNY')
  assert.match(plan.effectiveFrom, /Z$/u)
  assert.equal(plan.contractTermsJson.approved_by, 'cfo')

  const subscription = buildCommercialMutation('createSubscription', {
    subscriptionKey: 'sub-enterprise', status: 'active', startsAt: '2026-07-16T09:30',
    currentPeriodStart: '2026-07-16T09:30', currentPeriodEnd: '2026-08-16T09:30',
    seats: 20, metadataJson: '{}'
  }, { plan: { id: 'plan-1' } })
  assert.equal(subscription.planId, 'plan-1')
  assert.match(subscription.currentPeriodEnd, /Z$/u)

  const entitlement = buildCommercialMutation('upsertEntitlement', {
    entitlementKey: 'ocr', metricKey: 'ocr_calls', entitlementType: 'metered', unit: '次',
    includedQuantity: '100', hardLimitQuantity: '120', resetInterval: 'monthly',
    overagePolicy: 'block', effectiveFrom: '2026-07-16T09:30', configJson: '{}'
  }, { subscription: { id: 'sub-1' } })
  assert.equal(entitlement.subscriptionId, 'sub-1')
  assert.equal(entitlement.hardLimitQuantity, '120')
})

test('客户端在写入前拒绝不完整窗口、降配和无效 JSON', () => {
  assert.throws(() => buildCommercialMutation('createPlan', {
    planCode: 'p', name: 'P', pricingModel: 'subscription', billingInterval: 'annual', currency: 'CNY',
    baseFee: '0', includedSeats: 1, effectiveFrom: '2026-08-01T00:00', effectiveTo: '2026-07-01T00:00', contractTermsJson: '{}'
  }), /晚于/u)
  assert.throws(() => buildCommercialMutation('upsertEntitlement', {
    entitlementKey: 'ocr', metricKey: 'ocr', entitlementType: 'metered', unit: '次', includedQuantity: '100',
    hardLimitQuantity: '10', resetInterval: 'monthly', overagePolicy: 'block', effectiveFrom: '2026-07-01T00:00', configJson: '{}'
  }, { subscription: { id: 'sub-1' } }), /不能小于/u)
  assert.throws(() => buildCommercialMutation('createPlan', {
    planCode: 'p', name: 'P', pricingModel: 'subscription', billingInterval: 'annual', currency: 'CNY',
    baseFee: '0', includedSeats: 1, effectiveFrom: '2026-07-01T00:00', contractTermsJson: '{bad}'
  }), /有效 JSON/u)
})

test('用量和成本事件在客户端执行幂等、符号、冲回与币种边界校验', () => {
  const account = { subscription: { id: 'sub-1' } }
  const usage = buildCommercialMutation('recordUsage', {
    entitlementId: 'ent-1', eventType: 'usage', quantity: '1', occurredAt: '2026-07-16T09:30',
    sourceSystem: 'web', idempotencyKey: 'usage-1', metadataJson: '{}'
  }, account)
  assert.equal(usage.subscriptionId, 'sub-1')
  assert.match(usage.occurredAt, /Z$/u)

  assert.throws(() => buildCommercialMutation('recordUsage', {
    entitlementId: 'ent-1', eventType: 'credit', quantity: '1', occurredAt: '2026-07-16T09:30',
    sourceSystem: 'web', idempotencyKey: 'usage-2', metadataJson: '{}'
  }, account), /必须小于 0/u)
  assert.throws(() => buildCommercialMutation('recordUsage', {
    entitlementId: 'ent-1', eventType: 'reversal', quantity: '-1', occurredAt: '2026-07-16T09:30',
    sourceSystem: 'web', idempotencyKey: 'usage-3', metadataJson: '{}'
  }, account), /被冲回/u)

  const cost = buildCommercialMutation('recordCost', {
    eventType: 'incurred', costCategory: 'ai_inference', quantity: '3', unit: '次', unitCost: '0.02',
    originalCurrency: 'usd', reportingCurrency: 'cny', fxRate: '7.2', allocationKey: 'ai-july',
    occurredAt: '2026-07-16T09:30', sourceSystem: 'billing', idempotencyKey: 'cost-1', metadataJson: '{}'
  }, account)
  assert.equal(cost.originalCurrency, 'USD')
  assert.equal(cost.reportingCurrency, 'CNY')
  assert.equal(cost.fxRate, '7.2')
})

test('订阅状态变更必须使用版本、受限目标状态和可审计原因', () => {
  const transition = buildCommercialMutation('transitionSubscription', {
    resourceId: 'sub-1', expectedVersion: 3, targetStatus: 'suspended', reason: '客户合同暂停'
  })
  assert.deepEqual(transition, {
    resourceId: 'sub-1', expectedVersion: 3, targetStatus: 'suspended', reason: '客户合同暂停'
  })
  assert.throws(() => buildCommercialMutation('transitionSubscription', {
    resourceId: 'sub-1', expectedVersion: 3, targetStatus: 'active', reason: '恢复'
  }), /目标订阅状态无效/u)
  assert.throws(() => buildCommercialMutation('transitionSubscription', {
    resourceId: 'sub-1', expectedVersion: 3, targetStatus: 'canceled', reason: 'x'
  }), /至少填写 2/u)
})

function metric(key, label, values, status = 'available') {
  return { key, label, status, values, ratios: [], reason: '真实事实口径', requiredInputs: [], notes: [] }
}

function unavailable(key, label) {
  return { key, label, status: 'unavailable', values: [], ratios: [], reason: '缺少真实财务证据', requiredInputs: ['真实财务证据'], notes: [] }
}
