// @bun
import"./chunk-hhsxm2yr.js";

// src/server/sessionManager.ts
import { randomUUID } from "crypto";
import { createInterface } from "readline";
import { resolve } from "path";

class SessionManager {
  backend;
  idleTimeoutMs;
  maxSessions;
  sessions = new Map;
  constructor(backend, options = {}) {
    this.backend = backend;
    this.idleTimeoutMs = Math.max(0, options.idleTimeoutMs ?? 600000);
    this.maxSessions = Math.max(0, options.maxSessions ?? 32);
  }
  createSession(request) {
    this.enforceSessionLimit();
    const id = randomUUID();
    const workDir = resolve(request.cwd);
    const backendOptions = {
      sessionId: id,
      cwd: workDir,
      dangerouslySkipPermissions: request.dangerouslySkipPermissions
    };
    const backendSession = this.backend.createSession(backendOptions);
    const record = {
      id,
      status: "starting",
      createdAt: Date.now(),
      workDir,
      process: backendSession.process,
      backendSession,
      sockets: new Set,
      idleTimer: null,
      stderrTail: backendSession.stderrTail
    };
    this.sessions.set(id, record);
    this.pipeChildOutput(record);
    this.watchChildExit(record);
    return this.toInfo(record);
  }
  getSession(id) {
    const record = this.sessions.get(id);
    return record ? this.toInfo(record) : undefined;
  }
  listSessions() {
    return [...this.sessions.values()].map((record) => this.toInfo(record));
  }
  attachSocket(id, socket) {
    const record = this.sessions.get(id);
    if (!record || record.status === "stopped" || record.status === "stopping") {
      return false;
    }
    if (record.idleTimer) {
      clearTimeout(record.idleTimer);
      record.idleTimer = null;
    }
    record.sockets.add(socket);
    record.status = "running";
    return true;
  }
  detachSocket(id, socket) {
    const record = this.sessions.get(id);
    if (!record)
      return;
    record.sockets.delete(socket);
    if (record.sockets.size === 0 && record.status !== "stopped" && record.status !== "stopping") {
      record.status = "detached";
      this.scheduleIdleStop(record);
    }
  }
  receiveFromSocket(id, data) {
    const record = this.sessions.get(id);
    if (!record || !record.process.stdin || record.process.stdin.destroyed) {
      return false;
    }
    record.process.stdin.write(data.endsWith(`
`) ? data : `${data}
`);
    return true;
  }
  stopSession(id) {
    const record = this.sessions.get(id);
    if (!record)
      return false;
    this.stopRecord(record, "Session stopped");
    return true;
  }
  async destroyAll() {
    const records = [...this.sessions.values()];
    for (const record of records) {
      this.stopRecord(record, "Server shutting down");
    }
    await Promise.all(records.map((record) => new Promise((resolveClose) => {
      if (record.process.exitCode !== null || record.process.killed) {
        resolveClose();
        return;
      }
      record.process.once("close", () => resolveClose());
      setTimeout(resolveClose, 1000).unref();
    })));
  }
  enforceSessionLimit() {
    if (this.maxSessions === 0)
      return;
    const active = [...this.sessions.values()].filter((record) => record.status !== "stopped");
    if (active.length >= this.maxSessions) {
      throw new Error(`Maximum session limit reached (${this.maxSessions})`);
    }
  }
  pipeChildOutput(record) {
    record.process.stdout?.setEncoding("utf8");
    if (record.process.stdout) {
      const rl = createInterface({ input: record.process.stdout });
      rl.on("line", (line) => {
        if (record.status === "starting") {
          record.status = record.sockets.size > 0 ? "running" : "detached";
        }
        this.broadcast(record, `${line}
`);
      });
      rl.on("close", () => {
        if (record.sockets.size === 0 && record.status === "running") {
          record.status = "detached";
        }
      });
    }
  }
  watchChildExit(record) {
    record.process.once("close", () => {
      record.status = "stopped";
      if (record.idleTimer) {
        clearTimeout(record.idleTimer);
        record.idleTimer = null;
      }
      for (const socket of record.sockets) {
        socket.close(1000, "Session process exited");
      }
      record.sockets.clear();
      setTimeout(() => {
        this.sessions.delete(record.id);
      }, 30000).unref();
    });
  }
  scheduleIdleStop(record) {
    if (this.idleTimeoutMs === 0 || record.idleTimer)
      return;
    record.idleTimer = setTimeout(() => {
      record.idleTimer = null;
      if (record.sockets.size === 0 && record.status === "detached") {
        this.stopRecord(record, "Session idle timeout");
      }
    }, this.idleTimeoutMs);
    record.idleTimer.unref();
  }
  stopRecord(record, reason) {
    if (record.status === "stopped" || record.status === "stopping")
      return;
    record.status = "stopping";
    if (record.idleTimer) {
      clearTimeout(record.idleTimer);
      record.idleTimer = null;
    }
    for (const socket of record.sockets) {
      socket.close(1000, reason);
    }
    record.sockets.clear();
    if (!record.process.killed) {
      record.process.kill(process.platform === "win32" ? undefined : "SIGTERM");
    }
  }
  broadcast(record, data) {
    for (const socket of record.sockets) {
      try {
        socket.send(data);
      } catch {
        record.sockets.delete(socket);
      }
    }
  }
  toInfo(record) {
    return {
      id: record.id,
      status: record.status,
      createdAt: record.createdAt,
      workDir: record.workDir,
      process: record.process,
      sessionKey: record.sessionKey
    };
  }
}
export {
  SessionManager
};
