import { describe, expect, test } from "bun:test";
import { createPcm16ResamplerTransform, resamplePcm16 } from "./audio-transform.ts";
import type { AudioFrame } from "./types.ts";

describe("PCM16 audio transform", () => {
  test("upsamples 16 kHz mono PCM to 24 kHz for OpenAI Realtime", () => {
    const input = pcm16Bytes([0, 1600, 3200, 4800]);
    const output = resamplePcm16(input, 16000, 24000);

    expect(output.byteLength).toBe(12);
    expect(samples(output)[0]).toBe(0);
    expect(samples(output).at(-1)).toBe(4800);
  });

  test("downsamples provider PCM back to telephony PCM", async () => {
    const transform = createPcm16ResamplerTransform({
      telephonyFormat: { codec: "pcm16", sampleRateHz: 16000, channels: 1 },
      providerFormat: { codec: "pcm16", sampleRateHz: 24000, channels: 1 },
    });
    const providerFrame: AudioFrame = {
      data: pcm16Bytes([0, 1000, 2000, 3000, 4000, 5000]),
      format: { codec: "pcm16", sampleRateHz: 24000, channels: 1 },
    };

    const output = await transform.toTelephonyOutput(providerFrame);

    expect(output.format.sampleRateHz).toBe(16000);
    expect(output.data.byteLength).toBe(8);
  });

  test("rejects unsupported codec transforms", () => {
    expect(() =>
      createPcm16ResamplerTransform({
        telephonyFormat: { codec: "opus", sampleRateHz: 16000, channels: 1 },
        providerFormat: { codec: "pcm16", sampleRateHz: 24000, channels: 1 },
      }).toProviderInput({
        data: new Uint8Array([1, 2]),
        format: { codec: "opus", sampleRateHz: 16000, channels: 1 },
      }),
    ).toThrow(/Unsupported audio transform/);
  });
});

function pcm16Bytes(values: number[]): Uint8Array {
  const view = new DataView(new ArrayBuffer(values.length * 2));
  values.forEach((value, index) => view.setInt16(index * 2, value, true));
  return new Uint8Array(view.buffer);
}

function samples(bytes: Uint8Array): number[] {
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
  return Array.from({ length: bytes.byteLength / 2 }, (_, index) =>
    view.getInt16(index * 2, true),
  );
}
