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

import {
  buildOrganizationExpenseMemoryCreatePayload,
  buildOrganizationExpenseMemoryRevokePayload,
  buildOrganizationExpenseMemoryUpdatePayload,
  createOrganizationExpenseMemory,
  fetchOrganizationExpenseMemories,
  normalizeOrganizationExpenseMemory,
  revokeOrganizationExpenseMemory,
  updateOrganizationExpenseMemory
} from '../src/services/expenseApplicationMemories.js'

const settingsModel = readFileSync(new URL('../src/utils/settingsModelHelper.js', import.meta.url), 'utf8')
const settingsView = readFileSync(new URL('../src/views/SettingsView.vue', import.meta.url), 'utf8')
const settingsScript = readFileSync(new URL('../src/views/scripts/SettingsView.js', import.meta.url), 'utf8')
const memoryPanel = readFileSync(new URL('../src/views/ExpenseMemorySettingsPanel.vue', import.meta.url), 'utf8')

test('组织记忆 payload 限制低敏枚举、有效期和必填理由', () => {
  assert.deepEqual(buildOrganizationExpenseMemoryCreatePayload({
    scopeType: 'enterprise',
    scopeId: 'must-not-be-sent',
    value: '火车',
    expiresInDays: 180,
    reason: '统一企业基线',
    requestId: 'create-enterprise-1'
  }), {
    scope_type: 'enterprise',
    value: '火车',
    expires_in_days: 180,
    reason: '统一企业基线',
    request_id: 'create-enterprise-1'
  })
  assert.deepEqual(buildOrganizationExpenseMemoryCreatePayload({
    scopeType: 'department',
    scopeId: 'organization-unit-stable-id',
    value: '飞机',
    expiresInDays: 90,
    reason: '部门差旅基线',
    requestId: 'create-department-1'
  }), {
    scope_type: 'department',
    scope_id: 'organization-unit-stable-id',
    value: '飞机',
    expires_in_days: 90,
    reason: '部门差旅基线',
    request_id: 'create-department-1'
  })
  assert.deepEqual(buildOrganizationExpenseMemoryUpdatePayload({
    value: '轮船',
    expiresInDays: 120,
    expectedGeneration: 3,
    reason: '更新制度',
    requestId: 'update-memory-3'
  }), {
    value: '轮船',
    expires_in_days: 120,
    expected_generation: 3,
    reason: '更新制度',
    request_id: 'update-memory-3'
  })
  assert.deepEqual(buildOrganizationExpenseMemoryRevokePayload({
    expectedGeneration: 3,
    reason: '制度已停用',
    requestId: 'revoke-memory-3'
  }), {
    expected_generation: 3,
    reason: '制度已停用',
    request_id: 'revoke-memory-3'
  })
  assert.throws(
    () => buildOrganizationExpenseMemoryCreatePayload({
      scopeType: 'enterprise', value: '网约车', expiresInDays: 180, reason: '不应通过', requestId: 'invalid-value-1'
    }),
    /仅支持飞机、火车或轮船/
  )
  assert.throws(
    () => buildOrganizationExpenseMemoryCreatePayload({
      scopeType: 'enterprise', value: '火车', expiresInDays: 29, reason: '不应通过', requestId: 'invalid-days-1'
    }),
    /30 到 365 天/
  )
  assert.throws(
    () => buildOrganizationExpenseMemoryCreatePayload({
      scopeType: 'enterprise', value: '火车', expiresInDays: 180, reason: '', requestId: 'invalid-reason-1'
    }),
    /操作理由/
  )
})

test('组织记忆 service 使用管理员接口并保留作用域和版本解释', async () => {
  const requests = []
  const originalFetch = globalThis.fetch
  const responseMemory = {
    id: 'memory/enterprise 1',
    field_key: 'transport_mode',
    value: '火车',
    status: 'active',
    scope_type: 'enterprise',
    scope_id: 'tenant-1',
    scope_label: '企业统一规则',
    source: 'enterprise_policy_memory',
    origin_type: 'admin_managed',
    generation: 2,
    policy_version: 'expense_application_transport_org_memory.v1',
    expires_at: '2026-12-31T00:00:00Z',
    managed_by: 'private-admin-identifier',
    can_revoke: true
  }
  globalThis.fetch = async (url, options = {}) => {
    requests.push({ url: String(url), options })
    return {
      ok: true,
      status: 200,
      async json() {
        return requests.length === 1 ? { items: [responseMemory] } : responseMemory
      }
    }
  }

  try {
    const items = await fetchOrganizationExpenseMemories()
    await createOrganizationExpenseMemory({
      scopeType: 'enterprise', value: '火车', expiresInDays: 180, reason: '创建企业记忆', requestId: 'create-memory-1'
    })
    await updateOrganizationExpenseMemory('memory/enterprise 1', {
      value: '轮船', expiresInDays: 120, expectedGeneration: 2, reason: '更新记忆', requestId: 'update-memory-1'
    })
    await revokeOrganizationExpenseMemory('memory/enterprise 1', {
      expectedGeneration: 2, reason: '撤销记忆', requestId: 'revoke-memory-1'
    })

    assert.equal(items[0].scopeType, 'enterprise')
    assert.equal(items[0].generation, 2)
    assert.equal(items[0].sourceLabel, '管理员显式维护')
    assert.equal('managedBy' in items[0], false)
  } finally {
    globalThis.fetch = originalFetch
  }

  assert.equal(requests[0].url, '/api/v1/expense-application-memories/organization')
  assert.equal(requests[1].options.method, 'POST')
  assert.equal(
    requests[2].url,
    '/api/v1/expense-application-memories/organization/memory%2Fenterprise%201'
  )
  assert.equal(requests[2].options.method, 'PUT')
  assert.equal(
    requests[3].url,
    '/api/v1/expense-application-memories/organization/memory%2Fenterprise%201/revoke'
  )
  assert.equal(requests[3].options.method, 'POST')
})

test('组织记忆规范化不将管理员标识带入展示模型', () => {
  const memory = normalizeOrganizationExpenseMemory({
    id: 'memory-department-1',
    scope_type: 'department',
    scope_id: 'organization-unit-1',
    scope_label: '部门规则（财务部）',
    status: 'active',
    value: '飞机',
    origin_type: 'admin_managed',
    managed_by: 'sensitive-user-id'
  })
  assert.equal(memory.scopeId, 'organization-unit-1')
  assert.equal(memory.scopeLabel, '部门规则（财务部）')
  assert.equal(memory.sourceLabel, '管理员显式维护')
  assert.equal('managedBy' in memory, false)
})

test('设置页接入无顶部保存按钮的 AI 记忆独立面板', () => {
  assert.match(settingsModel, /id:\s*'aiMemory'[\s\S]*label:\s*'AI 记忆'[\s\S]*actionLabel:\s*''/)
  assert.match(settingsModel, /aiMemory:\s*true/)
  assert.match(settingsScript, /import ExpenseMemorySettingsPanel/)
  assert.match(settingsScript, /ExpenseMemorySettingsPanel,/)
  assert.match(settingsView, /activeSection === 'aiMemory'[\s\S]*<ExpenseMemorySettingsPanel \/>/)
  assert.match(memoryPanel, /fetchEmployeeMeta\(\)/)
  assert.match(memoryPanel, /item\?\.id && item\?\.unitType === 'department'/)
  assert.match(memoryPanel, /value:\s*String\(item\.id\)/)
  assert.match(memoryPanel, /飞机[、\s\S]*火车[、\s\S]*轮船/)
  assert.match(memoryPanel, /min="30" max="365"/)
  assert.match(memoryPanel, /第 \{\{ memory\.generation \}\} 版/)
  assert.match(memoryPanel, /memory\.sourceLabel/)
  assert.match(memoryPanel, /organization-memory-revoke:/)
  assert.match(memoryPanel, /requestId:\s*revokeRequestId\.value/)
  assert.doesNotMatch(memoryPanel, /managedBy|managed_by|managementReason|management_reason/)
})
