import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { fileURLToPath } from 'node:url'

import {
  executeRiskDispositionAction,
  normalizeRiskDisposition,
  normalizeRiskDispositionError,
  normalizeRiskObservation
} from '../src/services/riskObservations.js'

const evidenceCard = readFileSync(
  fileURLToPath(new URL('../src/components/travel/RiskObservationEvidenceCard.vue', import.meta.url)),
  'utf8'
)
const waiverDialog = readFileSync(
  fileURLToPath(new URL('../src/components/travel/RiskWaiverActionDialog.vue', import.meta.url)),
  'utf8'
)
const waiverRecord = readFileSync(
  fileURLToPath(new URL('../src/components/travel/RiskWaiverRecord.vue', import.meta.url)),
  'utf8'
)

function jsonResponse(payload) {
  return {
    ok: true,
    status: 200,
    async json() {
      return payload
    }
  }
}

test('risk disposition normalizes server-authorized actions and complete waiver audit fields', () => {
  const disposition = normalizeRiskDisposition({
    id: 'disposition-1',
    observation_id: 'risk-1',
    lifecycle_status: 'waived',
    version: 3,
    available_actions: ['approve_waiver', 'reject_waiver', 'approve_waiver'],
    read_only_reason: null,
    waiver_requester_id: 'employee-requester',
    waiver_requester_name: '申请人甲',
    waiver_requested_at: '2026-07-16T08:00:00Z',
    waiver_reason: '客户现场暂时无法取得原件',
    waiver_scope: '仅限本次单据的行程确认材料',
    waiver_expires_at: '2026-07-20T08:00:00Z',
    waiver_conditions_json: ['三日内补交原件', '到期前复核', '三日内补交原件'],
    waiver_decision: 'approved',
    waiver_decider_id: 'employee-decider',
    waiver_decider_name: '决定人乙',
    waiver_decided_at: '2026-07-16T09:00:00Z',
    waiver_decision_reason: '补偿控制充分，同意限时豁免',
    events: [{
      id: 'waiver-event-1',
      action: 'request_waiver',
      version: 2,
      actor_id: 'employee-requester',
      actor_name: '申请人甲',
      request_id: 'waiver-request-001',
      comment: '客户现场暂时无法取得原件',
      after_json: { waiver_scope: '仅限本次单据的行程确认材料' },
      created_at: '2026-07-16T08:00:00Z'
    }]
  })

  assert.deepEqual(disposition.availableActions, ['approve_waiver', 'reject_waiver'])
  assert.equal(disposition.readOnlyReason, '')
  assert.equal(disposition.waiverRequesterName, '申请人甲')
  assert.equal(disposition.waiverReason, '客户现场暂时无法取得原件')
  assert.equal(disposition.waiverScope, '仅限本次单据的行程确认材料')
  assert.deepEqual(disposition.waiverConditions, ['三日内补交原件', '到期前复核'])
  assert.equal(disposition.waiverDeciderName, '决定人乙')
  assert.equal(disposition.waiverDecisionReason, '补偿控制充分，同意限时豁免')
  assert.equal(disposition.events[0].actorId, 'employee-requester')
  assert.equal(disposition.events[0].requestId, 'waiver-request-001')
  assert.equal(
    disposition.events[0].afterState.waiver_scope,
    '仅限本次单据的行程确认材料'
  )
})

test('observation keeps server actions when the first disposition has not been created', () => {
  const observation = normalizeRiskDisposition(null)
  assert.equal(observation, null)

  const normalized = normalizeRiskObservation({
    id: 'risk-first-action',
    disposition: null,
    available_actions: ['confirm', 'false_positive'],
    read_only_reason: ''
  })
  assert.equal(normalized.disposition, null)
  assert.deepEqual(normalized.availableActions, ['confirm', 'false_positive'])
  assert.equal(normalized.readOnlyReason, '')
})

test('waiver request and decision payloads carry idempotency key and optimistic version', async (t) => {
  const originalFetch = globalThis.fetch
  const calls = []
  t.after(() => {
    globalThis.fetch = originalFetch
  })
  globalThis.fetch = async (url, options) => {
    calls.push({ url: String(url), body: JSON.parse(options.body) })
    return jsonResponse({
      disposition: {
        id: 'disposition-1',
        observation_id: 'risk/1',
        version: calls.length,
        available_actions: []
      },
      event: { id: `event-${calls.length}` },
      replayed: false
    })
  }

  await executeRiskDispositionAction('risk/1', {
    action: 'request_waiver',
    expectedVersion: 2,
    requestId: 'risk-request-waiver-001',
    waiverReason: '材料暂缺',
    waiverScope: '仅限当前单据',
    waiverExpiresAt: '2026-07-20T08:00:00.000Z',
    waiverConditions: ['三日内补件', '三日内补件', '', '财务复核']
  })
  await executeRiskDispositionAction('risk/1', {
    action: 'approve_waiver',
    expectedVersion: 3,
    requestId: 'risk-approve-waiver-001',
    comment: '补偿控制充分，同意限时豁免'
  })
  await executeRiskDispositionAction('risk/1', {
    action: 'reject_waiver',
    expectedVersion: 3,
    requestId: 'risk-reject-waiver-001',
    comment: '补偿控制不足，拒绝豁免'
  })

  assert.equal(calls[0].url, '/api/v1/risk-observations/risk%2F1/disposition/actions')
  assert.deepEqual(calls[0].body, {
    action: 'request_waiver',
    expected_version: 2,
    request_id: 'risk-request-waiver-001',
    waiver_reason: '材料暂缺',
    waiver_scope: '仅限当前单据',
    waiver_expires_at: '2026-07-20T08:00:00.000Z',
    waiver_conditions: ['三日内补件', '财务复核']
  })
  assert.deepEqual(calls[1].body, {
    action: 'approve_waiver',
    expected_version: 3,
    request_id: 'risk-approve-waiver-001',
    comment: '补偿控制充分，同意限时豁免'
  })
  assert.deepEqual(calls[2].body, {
    action: 'reject_waiver',
    expected_version: 3,
    request_id: 'risk-reject-waiver-001',
    comment: '补偿控制不足，拒绝豁免'
  })
})

test('waiver errors are presented by conflict, permission, validation and timeout type', () => {
  assert.deepEqual(
    normalizeRiskDispositionError({
      code: 'RISK_DISPOSITION_VERSION_CONFLICT',
      message: '状态已变化'
    }),
    {
      code: 'RISK_DISPOSITION_VERSION_CONFLICT',
      title: '风险状态已更新',
      message: '状态已变化',
      shouldRefresh: true
    }
  )
  assert.equal(
    normalizeRiskDispositionError({ code: 'RISK_WAIVER_DECISION_FORBIDDEN' }).title,
    '当前账号不能执行此操作'
  )
  assert.equal(normalizeRiskDispositionError({ status: 422 }).code, 'RISK_DISPOSITION_VALIDATION_FAILED')
  assert.equal(normalizeRiskDispositionError({ code: 'REQUEST_TIMEOUT' }).shouldRefresh, true)
})

test('waiver UI is server-action driven and makes all three confirmations explicit', () => {
  assert.match(evidenceCard, /currentDisposition\.value\.availableActions/)
  assert.match(evidenceCard, /currentDisposition\.readOnlyReason/)
  assert.match(evidenceCard, /批准豁免/)
  assert.match(evidenceCard, /拒绝豁免/)
  assert.match(evidenceCard, /风险豁免已拒绝，仍会阻断审批/)
  assert.match(evidenceCard, /waiverExpiresAt/)
  assert.match(evidenceCard, /scheduleWaiverExpiryRefresh/)
  assert.match(evidenceCard, /onBeforeUnmount\(clearWaiverExpiryTimer\)/)
  assert.doesNotMatch(evidenceCard, /roleCodes|role_codes|isAdmin|is_admin/)

  assert.match(waiverDialog, /确认提交豁免申请/)
  assert.match(waiverDialog, /确认批准豁免/)
  assert.match(waiverDialog, /确认拒绝豁免/)
  assert.match(waiverDialog, /请填写拒绝理由。/)
  assert.match(waiverDialog, /补偿条件最多填写 20 项。/)
  assert.match(waiverDialog, /拒绝后该风险仍会阻断审批/)
  assert.match(waiverDialog, /:disabled="busy"/)

  assert.match(waiverRecord, /申请人/)
  assert.match(waiverRecord, /申请理由/)
  assert.match(waiverRecord, /豁免范围/)
  assert.match(waiverRecord, /有效期至/)
  assert.match(waiverRecord, /补偿条件/)
  assert.match(waiverRecord, /决定记录/)
  assert.match(waiverRecord, /waiverDecisionReason/)
  assert.match(waiverRecord, /豁免审计轨迹/)
  assert.match(waiverRecord, /event\.requestId/)
})
