import assert from 'node:assert/strict'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import test from 'node:test'

import {
  getSkillRuntimeTools,
  looksLikeFakeToolExecutionClaim,
  recoverToolCallsFromAssistantText,
  runSkillTool,
} from './skill-run.js'

test('runtime tool manifest exposes real execution, office, search, html and browser tools', () => {
  const tools = getSkillRuntimeTools()
  const names = tools.map(tool => tool.name)

  for (const required of [
    'run_js',
    'create_word',
    'create_pdf',
    'create_excel',
    'create_pptx',
    'web_search',
    'fetch_url',
    'http_request',
    'create_html',
    'browser_automation',
    'browser_screenshot',
  ]) {
    assert.ok(names.includes(required), `${required} should be advertised`)
  }

  assert.equal(new Set(names).size, names.length, 'tool names should be unique')
  assert.ok(tools.every(tool => tool.group && tool.description), 'tools should be grouped and described')
})

test('create_html writes a complete html file into the session directory', async () => {
  const sessionDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aipros-html-tool-'))
  try {
    const result = await runSkillTool('create_html', {
      path: 'reports/index.html',
      title: '测试页面',
      bodyHtml: '<main><h1>真实 HTML</h1></main>',
      css: 'main { color: rgb(10, 20, 30); }',
      script: 'window.__ready = true;',
    }, sessionDir)

    assert.match(result, /HTML 文件 reports\/index\.html 已生成/)

    const html = fs.readFileSync(path.join(sessionDir, 'reports/index.html'), 'utf8')
    assert.match(html, /<!doctype html>/i)
    assert.match(html, /<title>测试页面<\/title>/)
    assert.match(html, /<main><h1>真实 HTML<\/h1><\/main>/)
    assert.match(html, /window\.__ready = true/)
  } finally {
    fs.rmSync(sessionDir, { recursive: true, force: true })
  }
})

test('create_pdf writes a real pdf file into the session directory', async () => {
  const sessionDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aipros-pdf-tool-'))
  try {
    const result = await runSkillTool('create_pdf', {
      path: 'reports/output.pdf',
      title: 'PDF 测试',
      content: '# PDF 测试\n\n这是一份真实生成的 PDF。',
    }, sessionDir)

    assert.match(result, /PDF 文件 reports\/output\.pdf 已生成/)

    const filePath = path.join(sessionDir, 'reports/output.pdf')
    const buf = fs.readFileSync(filePath)
    assert.equal(buf.subarray(0, 4).toString(), '%PDF')
    assert.ok(buf.length > 5000, 'pdf should contain rendered document bytes')
  } finally {
    fs.rmSync(sessionDir, { recursive: true, force: true })
  }
})

test('browser_screenshot protects local addresses before launching a browser', async () => {
  const sessionDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aipros-browser-tool-'))
  try {
    const result = await runSkillTool('browser_screenshot', {
      url: 'http://127.0.0.1:5180/all-skills',
      path: 'shot.png',
    }, sessionDir)

    assert.match(result, /不允许访问内网地址/)
    assert.equal(fs.existsSync(path.join(sessionDir, 'shot.png')), false)
  } finally {
    fs.rmSync(sessionDir, { recursive: true, force: true })
  }
})

test('fake tool claim detector catches prose that mimics tool calls and file generation', () => {
  assert.equal(
    looksLikeFakeToolExecutionClaim('调用工具：create_word(filename="process_document.docx", content="...")'),
    true,
  )
  assert.equal(
    looksLikeFakeToolExecutionClaim('✅ 文件已生成：process_document.docx'),
    true,
  )
  assert.equal(
    looksLikeFakeToolExecutionClaim('下一步可以按需生成 Word 文档。'),
    false,
  )
})

test('fallback recovery turns prose tool intent into executable tool args', () => {
  const calls = recoverToolCallsFromAssistantText([
    '调用工具：',
    'create_word(',
    '  filename="process_document.docx",',
    '  content="""',
    '# 采购付款流程制度',
    '',
    '## 1. 目的',
    '规范采购付款流程。',
    '"""',
    ')',
  ].join('\n'))

  assert.equal(calls.length, 1)
  assert.equal(calls[0].name, 'create_word')
  assert.equal(calls[0].args.path, 'process_document.docx')
  assert.match(calls[0].args.content, /采购付款流程制度/)
})

test('fallback recovery synthesizes a required tool call from the loaded skill contract', () => {
  const calls = recoverToolCallsFromAssistantText('正在调用 create_word 工具生成文档，正在生成 Word 文件...', {
    systemPrompt: '# 流程制度 Word 起草助手\n\n## 依赖工具\n- create_word：必须调用该工具生成最终 Word 文件',
    chatMessages: [{ role: 'user', content: '请生成采购付款流程制度。' }],
  })

  assert.equal(calls.length, 1)
  assert.equal(calls[0].name, 'create_word')
  assert.equal(calls[0].args.path, 'process_document.docx')
  assert.match(calls[0].args.content, /采购付款流程制度/)
  assert.match(calls[0].args.content, /样例版/)
})

test('fallback recovery selects the tool declared by the skill, not generic runner warnings', () => {
  const calls = recoverToolCallsFromAssistantText('正在调用 create_pdf 工具生成报告，文件已生成：process_review_report.pdf', {
    systemPrompt: [
      '禁止在正文里模拟工具调用格式，例如 create_word(...)、create_pdf(...)、create_excel(...)、create_html(...)。',
      '# 流程评审 PDF 报告助手',
      '## 依赖工具',
      '- create_pdf：必须调用该工具生成最终 PDF 文件',
    ].join('\n'),
    chatMessages: [{ role: 'user', content: '请生成入职流程评审报告。' }],
  })

  assert.equal(calls.length, 1)
  assert.equal(calls[0].name, 'create_pdf')
  assert.equal(calls[0].args.path, 'process_review_report.pdf')
})
