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 { fileURLToPath } from 'node:url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const workerRoot = path.resolve(__dirname, '../..')
const agentRunUrl = 'https://1242739972938523.agentrun-data.cn-hangzhou.aliyuncs.com/agent-runtimes/AIPRO-SP/endpoints/Default/invocations/openai/v1/chat/completions'

function sse(lines) {
  return new Response(lines.join('\r\n'), {
    status: 200,
    headers: { 'Content-Type': 'text/event-stream' },
  })
}

test('skill run sessions persist events and expose status for reconnect', async (t) => {
  const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'aipros-session-status-'))
  process.env.DB_PATH = path.join(tempRoot, 'test.db')
  process.env.OUTPUTS_DIR = path.join(tempRoot, 'outputs')

  const originalFetch = globalThis.fetch
  let upstreamCalls = 0
  globalThis.fetch = async () => {
    upstreamCalls += 1
    const content = upstreamCalls === 1 ? '长任务输出' : '第二轮输出'
    return sse([
    `data: {"choices":[{"delta":{"content":"${content}"},"finish_reason":"stop"}]}`,
    '',
    'data: [DONE]',
    '',
    ])
  }

  t.after(() => {
    globalThis.fetch = originalFetch
    fs.rmSync(tempRoot, { recursive: true, force: true })
  })

  const { default: app } = await import('../index.js')
  const { rawDB } = await import('../db.js')
  rawDB.exec(fs.readFileSync(path.join(workerRoot, 'migrations/0010_runtime_engines.sql'), 'utf8'))
  rawDB.exec(fs.readFileSync(path.join(workerRoot, 'migrations/0011_runtime_engine_api_key.sql'), 'utf8'))
  rawDB.prepare(`
    UPDATE runtime_engines
    SET enabled = 1,
        is_active = 1,
        endpoint_public_url = ?,
        api_key = ?
    WHERE code = 'agentrun'
  `).run(agentRunUrl, 'ak-secret')

  const res = await app.request('/api/skill-run/stream', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-User-Email': '494161546@qq.com',
    },
    body: JSON.stringify({ messages: [{ role: 'user', content: '跑一个长任务' }] }),
  })

  assert.equal(res.status, 200)
  const text = await res.text()
  const sessionMatch = text.match(/"sessionId":"([^"]+)"/)
  assert.ok(sessionMatch, 'stream should include session id')
  assert.match(text, /^id:\s*\d+/m, 'events should include stable ids for replay')
  assert.match(text, /长任务输出/)

  const statusRes = await app.request(`/api/skill-run/sessions/${sessionMatch[1]}/status`, {
    headers: { 'X-User-Email': '494161546@qq.com' },
  })

  assert.equal(statusRes.status, 200)
  const status = await statusRes.json()
  assert.equal(status.sessionId, sessionMatch[1])
  assert.equal(status.state, 'completed')
  assert.ok(Array.isArray(status.events))
  assert.ok(status.events.some(evt => evt.t === 'delta' && evt.v === '长任务输出'))
  assert.ok(Array.isArray(status.files))

  const secondRes = await app.request('/api/skill-run/stream', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-User-Email': '494161546@qq.com',
    },
    body: JSON.stringify({
      sessionId: sessionMatch[1],
      messages: [{ role: 'user', content: '第二轮继续' }],
    }),
  })

  assert.equal(secondRes.status, 200)
  const secondText = await secondRes.text()
  assert.match(secondText, /第二轮输出/)
  assert.doesNotMatch(secondText, /长任务输出/, 'new turns must not replay previous turn deltas')
})

test('completed skill run streams flush missed terminal events before closing', async (t) => {
  const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'aipros-session-terminal-'))
  const sessionDir = path.join(tempRoot, 'outputs', 'sid_terminal')
  fs.mkdirSync(sessionDir, { recursive: true })
  fs.writeFileSync(
    path.join(sessionDir, '.skill-run-events.jsonl'),
    [
      JSON.stringify({ id: 10, eventId: 10, sessionId: 'sid_terminal', t: 'delta', v: '最终输出' }),
      JSON.stringify({ id: 11, eventId: 11, sessionId: 'sid_terminal', t: 'done', state: 'completed' }),
    ].join('\n') + '\n',
    'utf8'
  )

  t.after(() => {
    fs.rmSync(tempRoot, { recursive: true, force: true })
  })

  const { flushMissedSkillRunTerminalEvents } = await import('./skill-run.js')
  const writes = []
  const stream = {
    writeSSE: async (evt) => {
      writes.push(evt)
    },
  }

  const flushed = await flushMissedSkillRunTerminalEvents({
    stream,
    sessionDir,
    afterEventId: 9,
  })

  assert.equal(flushed, 2)
  assert.match(writes[0].data, /最终输出/)
  assert.match(writes[1].data, /"done"/)
  assert.equal(writes[2].data, '[DONE]')
})
