import test from 'node:test'
import assert from 'node:assert/strict'
import {
  USE_CASE_EXAMPLE_DSL,
  buildDefaultUseCaseDsl,
  buildUseCaseDiagram,
  buildUseCaseExportSvg,
  parseUseCaseDsl
} from './useCaseDiagram.js'

const shoppingDsl = `用户
浏览商品
  搜索商品
  查看商品详情
  筛选商品
加入购物车
  选择规格
  修改数量
提交订单
  选择收货地址
  选择优惠券
评价商品
  上传图片?
  打分`

test('parseUseCaseDsl parses screenshot-style actor, use cases, and include relations', () => {
  const parsed = parseUseCaseDsl(shoppingDsl)

  assert.equal(parsed.actors.length, 1)
  assert.equal(parsed.actors[0].name, '用户')
  assert.equal(parsed.primaryUseCases.length, 4)
  assert.equal(parsed.useCases.length, 13)
  assert.equal(parsed.relations.filter((relation) => relation.type === 'association').length, 4)
  assert.equal(parsed.relations.filter((relation) => relation.type === 'include').length, 8)
  assert.equal(parsed.relations.filter((relation) => relation.type === 'extend').length, 1)
  assert.equal(parsed.warnings.length, 0)
  assert.deepEqual(
    parsed.primaryUseCases.map((item) => item.name),
    ['浏览商品', '加入购物车', '提交订单', '评价商品']
  )
})

test('parseUseCaseDsl normalizes tabs to two-space indentation', () => {
  const parsed = parseUseCaseDsl('用户\n浏览商品\n\t搜索商品')

  assert.equal(parsed.primaryUseCases.length, 1)
  assert.equal(parsed.useCases.find((item) => item.name === '搜索商品').parentId, 'use_case_1')
  assert.equal(parsed.relations.some((relation) => relation.type === 'include'), true)
})

test('parseUseCaseDsl marks question-suffixed children as extend relations', () => {
  const parsed = parseUseCaseDsl('用户\n支付订单\n  支付失败?')

  assert.equal(parsed.relations.find((relation) => relation.type === 'extend').target, 'use_case_1')
  assert.equal(parsed.useCases.find((item) => item.name === '支付失败').optional, true)
})

test('parseUseCaseDsl returns warnings for empty DSL and falls back to actor when missing', () => {
  const empty = parseUseCaseDsl('')
  assert.equal(empty.actors[0].name, '用户')
  assert.equal(empty.useCases.length, 0)
  assert.ok(empty.warnings.some((warning) => warning.includes('至少添加一个用例')))

  const missingActor = parseUseCaseDsl('  搜索商品')
  assert.equal(missingActor.actors[0].name, '用户')
  assert.equal(missingActor.primaryUseCases.length, 1)
  assert.ok(missingActor.warnings.some((warning) => warning.includes('缺少参与者')))
})

test('buildDefaultUseCaseDsl creates project-aware starter content from blueprint menus', () => {
  const dsl = buildDefaultUseCaseDsl({
    projectName: '图书借阅系统',
    appBlueprint: JSON.stringify({
      frontendMenus: [{ name: '图书浏览' }, { title: '我的借阅' }],
      adminMenus: [{ name: '图书管理' }, { code: 'borrowAudit' }]
    })
  })

  assert.match(dsl, /用户/)
  assert.match(dsl, /图书浏览/)
  assert.match(dsl, /我的借阅/)
  assert.match(dsl, /图书管理/)
  assert.match(dsl, /borrowAudit/)
})

test('buildUseCaseDiagram lays out actor, ovals, relations, and grows canvas', () => {
  const small = buildUseCaseDiagram(parseUseCaseDsl('用户\n浏览商品'))
  const large = buildUseCaseDiagram(parseUseCaseDsl(shoppingDsl), { title: '商城用例图' })

  assert.equal(large.title, '商城用例图')
  assert.equal(large.stats.actorCount, 1)
  assert.equal(large.stats.primaryUseCaseCount, 4)
  assert.equal(large.stats.useCaseCount, 13)
  assert.ok(large.canvas.height > small.canvas.height)
  assert.ok(large.actors[0].x < large.useCases[0].x)
  assert.ok(large.useCases.some((item) => item.kind === 'child'))
  assert.ok(large.relations.some((relation) => relation.label === '<<include>>'))
  assert.ok(large.relations.some((relation) => relation.label === '<<extend>>'))
})

test('buildUseCaseExportSvg serializes safe UML SVG without foreignObject', () => {
  const diagram = buildUseCaseDiagram(parseUseCaseDsl('用户\n浏览<script>\n  查看详情'), { title: '安全用例图' })
  const svg = buildUseCaseExportSvg(diagram)

  assert.match(svg, /安全用例图/)
  assert.match(svg, /&lt;script&gt;/)
  assert.match(svg, /ellipse/)
  assert.match(svg, /stroke-dasharray="5 4"/)
  assert.doesNotMatch(svg, /<script>/)
  assert.doesNotMatch(svg, /foreignObject/)
})

test('USE_CASE_EXAMPLE_DSL is valid default shopping example', () => {
  const parsed = parseUseCaseDsl(USE_CASE_EXAMPLE_DSL)

  assert.equal(parsed.warnings.length, 0)
  assert.ok(parsed.primaryUseCases.length >= 6)
  assert.ok(parsed.relations.some((relation) => relation.type === 'include'))
})
