// @vitest-environment jsdom
/**
 * test/queue.test.ts — W2 frontend inject-queue helper (public/queue.ts).
 *
 * fetch is mocked; every helper must NEVER throw (mirrors quick-reply's
 * never-throw discipline). Covers: POST url/body + parsed result, non-2xx →
 * {ok:false,status}, network reject → {ok:false}, DELETE, and the frame parser.
 */

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

import { enqueueFollowup, clearQueue, queueLengthFromFrame } from '../public/queue.js'

afterEach(() => {
  vi.restoreAllMocks()
  // @ts-expect-error — reset the mocked global between tests
  delete globalThis.fetch
})

function mockFetch(impl: (url: string, init?: RequestInit) => Promise<Response> | Response): void {
  globalThis.fetch = vi.fn(impl as unknown as typeof fetch)
}

describe('enqueueFollowup', () => {
  it('POSTs to the session queue URL with a JSON {text,appendEnter} body', async () => {
    const calls: Array<{ url: string; init?: RequestInit }> = []
    mockFetch((url, init) => {
      calls.push({ url, init })
      return new Response(JSON.stringify({ length: 1 }), { status: 200 })
    })

    const result = await enqueueFollowup('sess-1', 'run tests', true)

    expect(result).toEqual({ ok: true, length: 1 })
    expect(calls).toHaveLength(1)
    expect(calls[0]!.url).toBe('/live-sessions/sess-1/queue')
    expect(calls[0]!.init?.method).toBe('POST')
    expect(JSON.parse(String(calls[0]!.init?.body))).toEqual({
      text: 'run tests',
      appendEnter: true,
    })
  })

  it('returns {ok:false,status} on a non-2xx response and never throws', async () => {
    mockFetch(() => new Response('nope', { status: 409 }))
    const result = await enqueueFollowup('sess-1', 'x', false)
    expect(result).toEqual({ ok: false, status: 409 })
  })

  it('returns {ok:false} on a network reject and never throws', async () => {
    mockFetch(() => Promise.reject(new Error('offline')))
    const result = await enqueueFollowup('sess-1', 'x', false)
    expect(result).toEqual({ ok: false })
  })

  it('returns {ok:true} with undefined length when the body omits length', async () => {
    mockFetch(() => new Response(JSON.stringify({}), { status: 200 }))
    const result = await enqueueFollowup('sess-1', 'x', false)
    expect(result).toEqual({ ok: true, length: undefined })
  })
})

describe('clearQueue', () => {
  it('DELETEs the session queue URL and returns true on success', async () => {
    const calls: Array<{ url: string; init?: RequestInit }> = []
    mockFetch((url, init) => {
      calls.push({ url, init })
      return new Response(JSON.stringify({ length: 0 }), { status: 200 })
    })

    const ok = await clearQueue('sess-2')
    expect(ok).toBe(true)
    expect(calls[0]!.url).toBe('/live-sessions/sess-2/queue')
    expect(calls[0]!.init?.method).toBe('DELETE')
  })

  it('returns false on a non-2xx and on a network reject (never throws)', async () => {
    mockFetch(() => new Response('', { status: 404 }))
    expect(await clearQueue('sess-2')).toBe(false)

    mockFetch(() => Promise.reject(new Error('offline')))
    expect(await clearQueue('sess-2')).toBe(false)
  })
})

describe('queueLengthFromFrame', () => {
  it('returns the length for a {type:queue,length} frame', () => {
    expect(queueLengthFromFrame({ type: 'queue', length: 3 })).toBe(3)
  })

  it('returns null for unrelated or malformed frames', () => {
    expect(queueLengthFromFrame({ type: 'status', status: 'idle' })).toBeNull()
    expect(queueLengthFromFrame({ type: 'queue' })).toBeNull()
    expect(queueLengthFromFrame(null)).toBeNull()
    expect(queueLengthFromFrame('nope')).toBeNull()
  })
})
