"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var import_protocol = require("@livekit/protocol");
var net = __toESM(require("node:net"), 1);
var import_vitest = require("vitest");
var import_log = require("../log.cjs");
var import_remote_session = require("./remote_session.cjs");
(0, import_vitest.beforeAll)(() => {
  (0, import_log.initializeLogger)({ pretty: true, level: "info" });
});
function frame(msg) {
  const data = msg.toBinary();
  const header = Buffer.allocUnsafe(4);
  header.writeUInt32BE(data.length, 0);
  return Buffer.concat([header, Buffer.from(data)]);
}
function pingMessage(requestId) {
  return new import_protocol.AgentSession.AgentSessionMessage({
    message: {
      case: "request",
      value: new import_protocol.AgentSession.SessionRequest({
        requestId,
        request: { case: "ping", value: new import_protocol.AgentSession.SessionRequest_Ping() }
      })
    }
  });
}
async function listen(server) {
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
  return server.address().port;
}
(0, import_vitest.describe)("TcpSessionTransport framing", () => {
  let server;
  let transport;
  (0, import_vitest.afterEach)(async () => {
    await (transport == null ? void 0 : transport.close());
    transport = void 0;
    if (server) {
      await new Promise((resolve) => server.close(() => resolve()));
      server = void 0;
    }
  });
  (0, import_vitest.it)("decodes a single framed message", async () => {
    server = net.createServer((sock) => {
      sock.write(frame(pingMessage("r1")));
    });
    const port = await listen(server);
    transport = new import_remote_session.TcpSessionTransport("127.0.0.1", port);
    await transport.start();
    const it2 = transport[Symbol.asyncIterator]();
    const { value, done } = await it2.next();
    (0, import_vitest.expect)(done).toBe(false);
    (0, import_vitest.expect)(value.message.case).toBe("request");
    (0, import_vitest.expect)(value.message.value.requestId).toBe("r1");
  });
  (0, import_vitest.it)("reassembles a frame delivered across multiple chunks", async () => {
    const buf = frame(pingMessage("split"));
    server = net.createServer((sock) => {
      sock.write(buf.subarray(0, 5));
      setTimeout(() => sock.write(buf.subarray(5)), 10);
    });
    const port = await listen(server);
    transport = new import_remote_session.TcpSessionTransport("127.0.0.1", port);
    await transport.start();
    const it2 = transport[Symbol.asyncIterator]();
    const { value } = await it2.next();
    (0, import_vitest.expect)(value.message.value.requestId).toBe("split");
  });
  (0, import_vitest.it)("decodes multiple frames coalesced into one chunk", async () => {
    server = net.createServer((sock) => {
      sock.write(Buffer.concat([frame(pingMessage("a")), frame(pingMessage("b"))]));
    });
    const port = await listen(server);
    transport = new import_remote_session.TcpSessionTransport("127.0.0.1", port);
    await transport.start();
    const it2 = transport[Symbol.asyncIterator]();
    const first = await it2.next();
    const second = await it2.next();
    (0, import_vitest.expect)(first.value.message.value.requestId).toBe("a");
    (0, import_vitest.expect)(second.value.message.value.requestId).toBe("b");
  });
  (0, import_vitest.it)("round-trips a sent message back through the wire", async () => {
    const received = [];
    const sawFullFrame = new Promise((resolve) => {
      server = net.createServer((sock) => {
        sock.on("data", (d) => {
          received.push(d);
          resolve();
        });
      });
    });
    const port = await listen(server);
    transport = new import_remote_session.TcpSessionTransport("127.0.0.1", port);
    await transport.start();
    await transport.sendMessage(pingMessage("outbound"));
    await sawFullFrame;
    const wire = Buffer.concat(received);
    const length = wire.readUInt32BE(0);
    const decoded = import_protocol.AgentSession.AgentSessionMessage.fromBinary(wire.subarray(4, 4 + length));
    (0, import_vitest.expect)(decoded.message.value.requestId).toBe("outbound");
  });
  (0, import_vitest.it)("rejects start() when nothing is listening", async () => {
    const t = new import_remote_session.TcpSessionTransport("127.0.0.1", 1);
    await (0, import_vitest.expect)(t.start()).rejects.toThrow();
  });
  (0, import_vitest.it)("unblocks a backpressured sendMessage when the transport closes", async () => {
    let serverSocket;
    const localServer = net.createServer((sock) => {
      serverSocket = sock;
    });
    const port = await listen(localServer);
    const t = new import_remote_session.TcpSessionTransport("127.0.0.1", port);
    await t.start();
    try {
      const huge = "x".repeat(4 * 1024 * 1024);
      const msg = new import_protocol.AgentSession.AgentSessionMessage({
        message: {
          case: "request",
          value: new import_protocol.AgentSession.SessionRequest({
            requestId: huge,
            request: { case: "ping", value: new import_protocol.AgentSession.SessionRequest_Ping() }
          })
        }
      });
      const sendPromise = t.sendMessage(msg);
      await new Promise((r) => setTimeout(r, 50));
      await t.close();
      await (0, import_vitest.expect)(sendPromise).resolves.toBeUndefined();
    } finally {
      await t.close();
      serverSocket == null ? void 0 : serverSocket.destroy();
      await new Promise((resolve) => localServer.close(() => resolve()));
    }
  });
});
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;
        });
      }
    };
  }
}
(0, import_vitest.describe)("SessionHost updateIo", () => {
  (0, import_vitest.it)("toggles input/output enabled flags and acks", async () => {
    const setInputAudio = import_vitest.vi.fn();
    const setOutputAudio = import_vitest.vi.fn();
    const setTranscription = import_vitest.vi.fn();
    const fakeSession = {
      on: () => {
      },
      off: () => {
      },
      input: { setAudioEnabled: setInputAudio },
      output: {
        setAudioEnabled: setOutputAudio,
        setTranscriptionEnabled: setTranscription
      }
    };
    const transport = new FakeTransport();
    const host = new import_remote_session.SessionHost(transport);
    host.registerSession(fakeSession);
    await host.start();
    transport.push(
      new import_protocol.AgentSession.AgentSessionMessage({
        message: {
          case: "request",
          value: new import_protocol.AgentSession.SessionRequest({
            requestId: "io1",
            request: {
              case: "updateIo",
              value: new import_protocol.AgentSession.SessionRequest_UpdateIO({
                input: new import_protocol.AgentSession.SessionRequest_UpdateIO_Input({ audioEnabled: false }),
                output: new import_protocol.AgentSession.SessionRequest_UpdateIO_Output({
                  audioEnabled: true,
                  transcriptionEnabled: false
                })
              })
            }
          })
        }
      })
    );
    await import_vitest.vi.waitFor(() => (0, import_vitest.expect)(transport.sent.length).toBe(1));
    (0, import_vitest.expect)(setInputAudio).toHaveBeenCalledWith(false);
    (0, import_vitest.expect)(setOutputAudio).toHaveBeenCalledWith(true);
    (0, import_vitest.expect)(setTranscription).toHaveBeenCalledWith(false);
    const resp = transport.sent[0].message.value;
    (0, import_vitest.expect)(resp.requestId).toBe("io1");
    (0, import_vitest.expect)(resp.response.case).toBe("updateIo");
    await host.close();
  });
  (0, import_vitest.it)("ignores unset optional fields", async () => {
    const setInputAudio = import_vitest.vi.fn();
    const setOutputAudio = import_vitest.vi.fn();
    const setTranscription = import_vitest.vi.fn();
    const fakeSession = {
      on: () => {
      },
      off: () => {
      },
      input: { setAudioEnabled: setInputAudio },
      output: {
        setAudioEnabled: setOutputAudio,
        setTranscriptionEnabled: setTranscription
      }
    };
    const transport = new FakeTransport();
    const host = new import_remote_session.SessionHost(transport);
    host.registerSession(fakeSession);
    await host.start();
    transport.push(
      new import_protocol.AgentSession.AgentSessionMessage({
        message: {
          case: "request",
          value: new import_protocol.AgentSession.SessionRequest({
            requestId: "io2",
            request: {
              case: "updateIo",
              value: new import_protocol.AgentSession.SessionRequest_UpdateIO({
                output: new import_protocol.AgentSession.SessionRequest_UpdateIO_Output({ transcriptionEnabled: true })
              })
            }
          })
        }
      })
    );
    await import_vitest.vi.waitFor(() => (0, import_vitest.expect)(transport.sent.length).toBe(1));
    (0, import_vitest.expect)(setInputAudio).not.toHaveBeenCalled();
    (0, import_vitest.expect)(setOutputAudio).not.toHaveBeenCalled();
    (0, import_vitest.expect)(setTranscription).toHaveBeenCalledWith(true);
    await host.close();
  });
});
//# sourceMappingURL=remote_session.test.cjs.map