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

import { runSkillRunStreamWithReconnect } from './skillRunStream.js'

function streamResponse(chunks, options = {}) {
  const encoder = new TextEncoder()
  let index = 0
  return new Response(new ReadableStream({
    pull(controller) {
      if (index < chunks.length) {
        controller.enqueue(encoder.encode(chunks[index]))
        index += 1
        return
      }
      if (options.failAfterChunks) controller.error(new Error('network dropped'))
      else {
        controller.close()
      }
    },
  }), {
    status: 200,
    headers: { 'Content-Type': 'text/event-stream' },
  })
}

test('runSkillRunStreamWithReconnect resumes an interrupted stream from the last event id', async (t) => {
  const originalFetch = globalThis.fetch
  const bodies = []
  let calls = 0

  globalThis.fetch = async (_url, init) => {
    calls += 1
    bodies.push(JSON.parse(init.body))
    if (calls === 1) {
      return streamResponse([
        'id: 1\ndata: {"t":"session","sessionId":"sid_retry"}\n\n',
        'id: 2\ndata: {"t":"delta","v":"第一段"}\n\n',
      ], { failAfterChunks: true })
    }
    return streamResponse([
      'id: 2\ndata: {"t":"resume","sessionId":"sid_retry","fromEventId":2,"replayed":0}\n\n',
      'id: 3\ndata: {"t":"delta","v":"第二段"}\n\n',
      'id: 4\ndata: {"t":"done","sessionId":"sid_retry","state":"completed"}\n\n',
      'data: [DONE]\n\n',
    ])
  }

  t.after(() => {
    globalThis.fetch = originalFetch
  })

  const deltas = []
  const reconnects = []
  await runSkillRunStreamWithReconnect({
    apiBase: 'http://example.test/api',
    body: { messages: [{ role: 'user', content: '跑长任务' }] },
    retryDelayMs: 0,
    maxReconnects: 2,
    handlers: {
      onDelta: (v) => deltas.push(v),
      onReconnect: (info) => reconnects.push(info),
    },
  })

  assert.equal(calls, 2)
  assert.deepEqual(deltas, ['第一段', '第二段'])
  assert.equal(bodies[1].resume, true)
  assert.equal(bodies[1].sessionId, 'sid_retry')
  assert.equal(bodies[1].resumeFromEventId, 2)
  assert.equal(reconnects.length, 1)
})
