import { afterEach, describe, expect, it, vi } from 'vitest'

import { RevolutionaryTypedFetch } from '../src/core/typed-fetch.js'

const createChunkedStream = (chunks: number[]) => {
  let index = 0
  return {
    getReader() {
      return {
        async read() {
          if (index >= chunks.length) {
            return { done: true as const, value: undefined }
          }
          const size = chunks[index++] ?? 0
          return { done: false as const, value: new Uint8Array(size || 0) }
        },
        async cancel() {
          /* noop */
        }
      }
    }
  }
}

describe('throttled helper', () => {
  afterEach(() => {
    vi.restoreAllMocks()
  })

  it('throttles response streams before delivering subsequent chunks', async () => {
    const tf = new RevolutionaryTypedFetch()
    let current = 0
    const sleep = vi.fn(async (ms: number) => {
      current += ms
    })

    const stream = createChunkedStream([1024, 1024])
    const throttled = await tf.throttled(() => Promise.resolve(stream), {
      bandwidth: '1KB/s',
      sleep,
      now: () => current
    })
    expect(typeof throttled.getReader).toBe('function')

    const reader = throttled.getReader()
    const first = await reader.read()
    expect(first.value?.length).toBe(1024)

    const second = await reader.read()
    expect(second.value?.length).toBe(1024)
    expect(sleep).toHaveBeenCalled()

    const final = await reader.read()
    expect(final.done).toBe(true)
  })

  it('wraps nested response objects without touching data payloads', async () => {
    const tf = new RevolutionaryTypedFetch()
    let current = 0
    const sleep = vi.fn(async (ms: number) => {
      current += ms
    })

    const original = {
      body: createChunkedStream([2048, 2048]),
      headers: new Headers({ 'X-Test': '1' }),
      status: 202,
      statusText: 'Accepted'
    }
    const payload = { data: { ok: true }, response: original }

    const result = await tf.throttled(() => Promise.resolve(payload), {
      bandwidth: '1KB/s',
      sleep,
      now: () => current
    })

    expect(result.data).toEqual({ ok: true })
    expect(result.response).not.toBe(original)
    expect(result.response.status).toBe(202)

    const reader = result.response.body!.getReader()
    await reader.read()
    await reader.read()
    const tail = await reader.read()
    expect(tail.done).toBe(true)
  })
})
