import { afterEach, describe, expect, it, vi } from "vitest";

import { VoiceCallPlayer } from "@/app/call/voice-call-player";

describe("VoiceCallPlayer", () => {
  afterEach(() => {
    vi.unstubAllGlobals();
  });

  it("schedules decoded sentence audio on one continuous Web Audio timeline", async () => {
    const starts: number[] = [];
    const sources: Array<{ onended: (() => void) | null }> = [];
    const context = {
      state: "running",
      currentTime: 0,
      destination: {},
      resume: vi.fn(async () => undefined),
      close: vi.fn(async () => undefined),
      decodeAudioData: vi.fn(async () => ({ duration: 1 })),
      createBufferSource: vi.fn(() => {
        const source = {
          buffer: null,
          onended: null as (() => void) | null,
          connect: vi.fn(),
          disconnect: vi.fn(),
          stop: vi.fn(),
          start: vi.fn((at: number) => starts.push(at)),
        };
        sources.push(source);
        return source;
      }),
    };
    vi.stubGlobal("AudioContext", function AudioContextMock() {
      return context;
    });

    const player = new VoiceCallPlayer();
    const gaps: number[] = [];
    player.onGap = (milliseconds) => gaps.push(milliseconds);
    const meta = {
      callId: "call-1",
      turnId: "turn-1",
      index: 0,
      mimeType: "audio/mpeg",
      byteLength: 3,
    };

    player.enqueue(meta, new Uint8Array([1, 2, 3]).buffer);
    player.enqueue({ ...meta, index: 1 }, new Uint8Array([4, 5, 6]).buffer);

    await vi.waitFor(() => expect(starts).toHaveLength(2));
    expect(starts).toEqual([0.025, 1.025]);
    expect(gaps).toEqual([0]);
    expect(sources).toHaveLength(2);
    player.dispose();
  });
});
