import { describe, expect, it } from 'vitest';
import { sniffAndDecodeBody } from './web.js';

function fakeResponse(chunks: Uint8Array[]): { body: ReadableStream<Uint8Array> } {
  let i = 0;
  const body = new ReadableStream<Uint8Array>({
    pull(controller) {
      if (i < chunks.length) controller.enqueue(chunks[i++]);
      else controller.close();
    },
  });
  return { body };
}
const u8 = (...bytes: number[]) => Uint8Array.from(bytes);
const u8txt = (s: string) => new TextEncoder().encode(s);

describe('sniffAndDecodeBody', () => {
  it('returns text for plain HTML', async () => {
    const r = await sniffAndDecodeBody(fakeResponse([u8txt('<html>hi</html>')]) as never);
    expect(r).toEqual({ binary: false, text: '<html>hi</html>', truncated: false });
  });
  it('blocks an OLE2 (.xls) body even split across chunks', async () => {
    const r = await sniffAndDecodeBody(
      fakeResponse([u8(0xD0, 0xCF, 0x11, 0xE0), u8(0xA1, 0xB1, 0x1A, 0xE1, 0x00)]) as never,
    );
    expect(r).toEqual({ binary: true, reason: 'magic:ole2' });
  });
  it('blocks a body with NUL bytes', async () => {
    const r = await sniffAndDecodeBody(fakeResponse([u8(0x68, 0x69, 0x00, 0x68)]) as never);
    expect(r).toEqual({ binary: true, reason: 'nul-byte' });
  });
  it('caps an oversized text body and marks it truncated', async () => {
    const big = u8txt('a'.repeat(6 * 1024 * 1024));
    const r = await sniffAndDecodeBody(fakeResponse([big]) as never);
    expect(r.binary).toBe(false);
    if (!r.binary) {
      expect(r.truncated).toBe(true);
      expect(r.text.length).toBeLessThanOrEqual(5 * 1024 * 1024);
    }
  });
  it('handles an empty body as empty text', async () => {
    const r = await sniffAndDecodeBody(fakeResponse([]) as never);
    expect(r).toEqual({ binary: false, text: '', truncated: false });
  });
  it('blocks bytes that decode-fail as utf-8 (no NUL, no BOM)', async () => {
    const r = await sniffAndDecodeBody(fakeResponse([u8(0x41, 0xC0, 0xC1, 0x80, 0x42)]) as never);
    expect(r.binary).toBe(true);
  });
  it('passes a UTF-16LE BOM body through as text', async () => {
    // FF FE BOM + "hi" in UTF-16LE
    const r = await sniffAndDecodeBody(fakeResponse([u8(0xFF, 0xFE, 0x68, 0x00, 0x69, 0x00)]) as never);
    expect(r).toEqual({ binary: false, text: 'hi', truncated: false });
  });
  it('blocks a small (<8KB) body via control-char ratio (post-loop re-sniff path)', async () => {
    const r = await sniffAndDecodeBody(fakeResponse([u8(0x01, 0x02, 0x03, 0x04, 0x05, 0x41)]) as never);
    expect(r.binary).toBe(true);
  });
});
