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

import {
  ASSISTANT_SCOPE_ACTION_FILL_COMPOSER
} from '../src/utils/assistantSessionScope.js'
import {
  buildStewardPlanMessageText,
  buildStewardSuggestedActions,
  isOffTopicStewardPlan,
  normalizeStewardPlan
} from '../src/views/scripts/stewardPlanModel.js'
import {
  resolveSuggestedActionPrefill
} from '../src/utils/assistantSuggestedActionPrefill.js'

const OFF_TOPIC_PLAN = {
  plan_id: 'steward-plan-off-topic',
  plan_status: 'off_topic',
  next_action: 'none',
  tasks: [],
  attachment_groups: [],
  confirmation_groups: [],
  candidate_flows: [],
  summary: '很抱歉主人，目前小财管家只能帮您整理**费用申请**和**费用报销**这两类事项。',
  suggested_prompts: [
    '我想要申请明天去北京出差3天，支撑客户现场实施',
    '我要报销上周去上海的高铁票',
    '差旅住宿标准是多少'
  ],
  thinking_events: [
    {
      event_id: 'off-topic-explain',
      stage: 'intent_review',
      title: '未识别到财务业务意图',
      content: '用户输入的内容没有匹配到费用申请或费用报销相关的业务信号。',
      status: 'completed'
    }
  ]
}

test('normalizeStewardPlan passes suggested_prompts through as trimmed strings', () => {
  const normalized = normalizeStewardPlan({
    plan_id: 'p-1',
    plan_status: 'off_topic',
    suggested_prompts: [
      '  申请出差  ',
      '',
      null,
      '报销高铁票'
    ]
  })
  assert.deepEqual(normalized.suggestedPrompts, ['申请出差', '报销高铁票'])
})

test('normalizeStewardPlan falls back to empty suggested prompts when missing', () => {
  const normalized = normalizeStewardPlan({ plan_id: 'p-2', plan_status: 'off_topic' })
  assert.deepEqual(normalized.suggestedPrompts, [])
})

test('isOffTopicStewardPlan returns true only for off_topic plan status', () => {
  assert.equal(isOffTopicStewardPlan({ plan_status: 'off_topic' }), true)
  assert.equal(isOffTopicStewardPlan({ plan_status: 'needs_flow_confirmation' }), false)
  assert.equal(isOffTopicStewardPlan({}), false)
})

test('buildStewardPlanMessageText renders backend-provided off_topic summary verbatim', () => {
  // off_topic 文案由后端（含 LLM）生成，前端只负责透传，不再拼接标题/引导
  const text = buildStewardPlanMessageText(OFF_TOPIC_PLAN)
  assert.equal(text, OFF_TOPIC_PLAN.summary)
  // 推荐话术本身不在正文里展示，而是作为按钮单独渲染，避免重复。
  for (const prompt of OFF_TOPIC_PLAN.suggested_prompts) {
    assert.equal(text.includes(prompt), false, `正文不应包含推荐话术：${prompt}`)
  }
})

test('buildStewardPlanMessageText adapts greeting vs meaningless vs off_business summaries', () => {
  // 问候场景：礼貌回应主人
  const greetingText = buildStewardPlanMessageText({
    plan_id: 'p-greeting',
    plan_status: 'off_topic',
    next_action: 'none',
    summary: '### 您好主人，很高兴为您服务\n\n请问您今天要办理什么业务？',
    suggested_prompts: ['我想要申请明天去北京出差3天']
  })
  assert.match(greetingText, /您好主人/)
  assert.match(greetingText, /请问您今天要办理什么业务/)

  // 无意义场景：温和解释 + 引导换种说法
  const meaninglessText = buildStewardPlanMessageText({
    plan_id: 'p-meaningless',
    plan_status: 'off_topic',
    next_action: 'none',
    summary: '### 这句话我暂时没识别到财务事项\n\n很抱歉主人，目前小财管家只能帮您整理**费用申请**和**费用报销**。',
    suggested_prompts: ['我想要申请明天去北京出差3天']
  })
  assert.match(meaninglessText, /这句话我暂时没识别到财务事项/)
  assert.match(meaninglessText, /很抱歉主人/)

  // 有意义但非业务场景：LLM 生成的文案（这里 mock 模拟）
  const llmText = '### 抱歉主人，这句话我暂时帮不上忙\n\n主人聊的是天气，小财管家目前只能帮您整理**费用申请**和**费用报销**。'
  const offBusinessText = buildStewardPlanMessageText({
    plan_id: 'p-off-business',
    plan_status: 'off_topic',
    next_action: 'none',
    summary: llmText,
    suggested_prompts: ['我想要申请明天去北京出差3天']
  })
  assert.equal(offBusinessText, llmText)
})

test('buildStewardPlanMessageText falls back to client template when summary is missing', () => {
  // 后端 summary 缺失时，前端有兜底文案保证体验不空白
  const text = buildStewardPlanMessageText({
    plan_id: 'p-empty',
    plan_status: 'off_topic',
    next_action: 'none',
    suggested_prompts: []
  })
  assert.match(text, /这句话我暂时没识别到财务事项/)
  assert.match(text, /费用申请.*费用报销|费用报销.*费用申请/)
})

test('buildStewardSuggestedActions returns fill_composer actions for off_topic plan', () => {
  const actions = buildStewardSuggestedActions(OFF_TOPIC_PLAN)
  assert.equal(actions.length, OFF_TOPIC_PLAN.suggested_prompts.length)
  for (const action of actions) {
    assert.equal(action.action_type, ASSISTANT_SCOPE_ACTION_FILL_COMPOSER)
    assert.equal(typeof action.payload.fill_text, 'string')
    assert.ok(action.payload.fill_text.length > 0)
    assert.equal(action.payload.steward_plan_id, OFF_TOPIC_PLAN.plan_id)
  }
})

test('buildStewardSuggestedActions truncates long off_topic prompt labels', () => {
  const longPrompt = '我想要申请明天去北京出差3天，支撑客户现场实施，需要预订酒店和高铁票'
  const actions = buildStewardSuggestedActions({
    plan_id: 'p-long',
    plan_status: 'off_topic',
    suggested_prompts: [longPrompt]
  })
  assert.equal(actions.length, 1)
  assert.ok(actions[0].label.length <= 27) // 24 字符 + "..."
  assert.ok(actions[0].label.endsWith('...'))
  // payload.fill_text 必须保留完整话术，不被截断
  assert.equal(actions[0].payload.fill_text, longPrompt)
})

test('buildStewardSuggestedActions keeps short off_topic prompt labels intact', () => {
  const shortPrompt = '差旅住宿标准是多少'
  const actions = buildStewardSuggestedActions({
    plan_id: 'p-short',
    plan_status: 'off_topic',
    suggested_prompts: [shortPrompt]
  })
  assert.equal(actions[0].label, shortPrompt)
  assert.equal(actions[0].payload.fill_text, shortPrompt)
})

test('buildStewardSuggestedActions returns empty array for off_topic plan without prompts', () => {
  const actions = buildStewardSuggestedActions({
    plan_id: 'p-empty',
    plan_status: 'off_topic',
    suggested_prompts: []
  })
  assert.deepEqual(actions, [])
})

test('off_topic fill_composer action is resolved as composer prefill (fill not submit)', () => {
  const actions = buildStewardSuggestedActions(OFF_TOPIC_PLAN)
  const firstAction = actions[0]
  const prefill = resolveSuggestedActionPrefill(firstAction)
  assert.equal(prefill, OFF_TOPIC_PLAN.suggested_prompts[0])
})

test('resolveSuggestedActionPrefill reads payload.fill_text directly', () => {
  assert.equal(
    resolveSuggestedActionPrefill({
      action_type: ASSISTANT_SCOPE_ACTION_FILL_COMPOSER,
      payload: { fill_text: '帮我申请下周出差' }
    }),
    '帮我申请下周出差'
  )
  // 空字符串/空白应被忽略
  assert.equal(
    resolveSuggestedActionPrefill({
      action_type: ASSISTANT_SCOPE_ACTION_FILL_COMPOSER,
      payload: { fill_text: '   ' }
    }),
    ''
  )
})

test('off_topic branch does not break pending flow confirmation actions', () => {
  // pending flow 不应被 off_topic 分支拦截
  const actions = buildStewardSuggestedActions({
    plan_id: 'steward-plan-pending-flow',
    plan_status: 'needs_flow_confirmation',
    next_action: 'confirm_flow',
    pending_flow_confirmation: {
      status: 'pending',
      reason: '缺少申请或报销动作词。',
      candidate_flows: [
        {
          flow_id: 'travel_application',
          label: '补办出差申请',
          confidence: 0.6,
          ontology_fields: { location: '上海' },
          missing_fields: []
        }
      ]
    }
  })
  assert.equal(actions.length, 1)
  assert.equal(actions[0].payload.flow_id, 'travel_application')
})
