"use strict";
var import_protocol = require("@livekit/protocol");
var import_rtc_node = require("@livekit/rtc-node");
var import_vitest = require("vitest");
var import_log = require("../log.cjs");
var import_console_io = require("./console_io.cjs");
var import_remote_session = require("./remote_session.cjs");
(0, import_vitest.beforeAll)(() => {
  (0, import_log.initializeLogger)({ pretty: true, level: "info" });
});
class FakeTransport extends import_remote_session.SessionTransport {
  sent = [];
  inbound = [];
  waitingResolve = null;
  closed = false;
  push(msg) {
    if (this.waitingResolve) {
      const resolve = this.waitingResolve;
      this.waitingResolve = null;
      resolve({ value: msg, done: false });
    } else {
      this.inbound.push(msg);
    }
  }
  async sendMessage(msg) {
    this.sent.push(msg);
  }
  async close() {
    this.closed = true;
    if (this.waitingResolve) {
      const resolve = this.waitingResolve;
      this.waitingResolve = null;
      resolve({ value: void 0, done: true });
    }
  }
  [Symbol.asyncIterator]() {
    return {
      next: () => {
        const pending = this.inbound.shift();
        if (pending) return Promise.resolve({ value: pending, done: false });
        if (this.closed) {
          return Promise.resolve({
            value: void 0,
            done: true
          });
        }
        return new Promise((resolve) => {
          this.waitingResolve = resolve;
        });
      }
    };
  }
}
function consoleFrame(sampleRate, samples) {
  return new import_protocol.AgentSession.AgentSessionMessage_ConsoleIO_AudioFrame({
    data: new Uint8Array(samples * 2),
    sampleRate,
    numChannels: 1,
    samplesPerChannel: samples
  });
}
(0, import_vitest.describe)("TcpAudioInput", () => {
  (0, import_vitest.it)("resamples wire frames to the agent rate and exposes them on the stream", async () => {
    const input = new import_console_io.TcpAudioInput();
    const reader = input.stream.getReader();
    input.pushFrame(consoleFrame(48e3, 48e3));
    const { value, done } = await reader.read();
    (0, import_vitest.expect)(done).toBe(false);
    (0, import_vitest.expect)(value.sampleRate).toBe(24e3);
    (0, import_vitest.expect)(value.channels).toBe(1);
    reader.releaseLock();
    await input.close();
  });
  (0, import_vitest.it)("drops frames pushed after close", async () => {
    const input = new import_console_io.TcpAudioInput();
    await input.close();
    (0, import_vitest.expect)(() => input.pushFrame(consoleFrame(48e3, 48e3))).not.toThrow();
  });
});
(0, import_vitest.describe)("TcpAudioOutput", () => {
  function agentFrame(seconds) {
    const samples = 24e3 * seconds;
    return new import_rtc_node.AudioFrame(new Int16Array(samples), 24e3, 1, samples);
  }
  (0, import_vitest.it)("streams resampled frames and completes the flush handshake on playout finished", async () => {
    const transport = new FakeTransport();
    const out = new import_console_io.TcpAudioOutput(transport);
    await out.captureFrame(agentFrame(1));
    out.flush();
    const playout = out.waitForPlayout();
    out.notifyPlayoutFinished();
    const ev = await playout;
    (0, import_vitest.expect)(ev.interrupted).toBe(false);
    (0, import_vitest.expect)(ev.playbackPosition).toBeCloseTo(1, 1);
    (0, import_vitest.expect)(transport.sent.some((m) => m.message.case === "audioOutput")).toBe(true);
    (0, import_vitest.expect)(transport.sent.some((m) => m.message.case === "audioPlaybackFlush")).toBe(true);
  });
  (0, import_vitest.it)("reports interruption when the buffer is cleared mid-playout", async () => {
    const transport = new FakeTransport();
    const out = new import_console_io.TcpAudioOutput(transport);
    await out.captureFrame(agentFrame(1));
    out.flush();
    const playout = out.waitForPlayout();
    out.clearBuffer();
    const ev = await playout;
    (0, import_vitest.expect)(ev.interrupted).toBe(true);
    (0, import_vitest.expect)(ev.playbackPosition).toBeLessThan(1);
    (0, import_vitest.expect)(transport.sent.some((m) => m.message.case === "audioPlaybackClear")).toBe(true);
  });
});
(0, import_vitest.describe)("AgentsConsole", () => {
  (0, import_vitest.afterEach)(() => {
    import_console_io.AgentsConsole._reset();
  });
  const makeSession = () => ({
    input: { audio: {} },
    output: { audio: {}, transcription: {} }
  });
  (0, import_vitest.it)("is a process-wide singleton", () => {
    (0, import_vitest.expect)(import_console_io.AgentsConsole.getInstance()).toBe(import_console_io.AgentsConsole.getInstance());
  });
  (0, import_vitest.it)("acquireIo attaches the audio bridges (voice default) and flips ioAcquired", () => {
    const c = import_console_io.AgentsConsole.getInstance();
    (0, import_vitest.expect)(c.ioAcquired).toBe(false);
    const audioInput = {};
    const audioOutput = {};
    c.audioInput = audioInput;
    c.audioOutput = audioOutput;
    const session = makeSession();
    c.acquireIo(session);
    (0, import_vitest.expect)(c.ioAcquired).toBe(true);
    (0, import_vitest.expect)(session.input.audio).toBe(audioInput);
    (0, import_vitest.expect)(session.output.audio).toBe(audioOutput);
    (0, import_vitest.expect)(session.output.transcription).toBeNull();
  });
  (0, import_vitest.it)("throws when acquired by a second session", () => {
    const c = import_console_io.AgentsConsole.getInstance();
    c.acquireIo(makeSession());
    (0, import_vitest.expect)(() => c.acquireIo(makeSession())).toThrow(/already acquired/);
  });
});
(0, import_vitest.describe)("SessionHost audio routing", () => {
  (0, import_vitest.it)("routes audioInput to pushFrame and audioPlaybackFinished to notifyPlayoutFinished", async () => {
    const transport = new FakeTransport();
    const pushFrame = import_vitest.vi.fn();
    const notifyPlayoutFinished = import_vitest.vi.fn();
    const audioInput = { pushFrame };
    const audioOutput = { notifyPlayoutFinished };
    const host = new import_remote_session.SessionHost(transport, audioInput, audioOutput);
    await host.start();
    transport.push(
      new import_protocol.AgentSession.AgentSessionMessage({
        message: { case: "audioInput", value: consoleFrame(48e3, 480) }
      })
    );
    transport.push(
      new import_protocol.AgentSession.AgentSessionMessage({
        message: {
          case: "audioPlaybackFinished",
          value: new import_protocol.AgentSession.AgentSessionMessage_ConsoleIO_AudioPlaybackFinished()
        }
      })
    );
    await import_vitest.vi.waitFor(() => {
      (0, import_vitest.expect)(pushFrame).toHaveBeenCalledTimes(1);
      (0, import_vitest.expect)(notifyPlayoutFinished).toHaveBeenCalledTimes(1);
    });
    await host.close();
  });
});
//# sourceMappingURL=console_io.test.cjs.map