"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 __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var session_exports = {};
__export(session_exports, {
  Session: () => Session,
  renderResolvedConfig: () => renderResolvedConfig
});
module.exports = __toCommonJS(session_exports);
var import_child_process = require("child_process");
var import_fs = __toESM(require("fs"));
var import_net = __toESM(require("net"));
var import_os = __toESM(require("os"));
var import_path = __toESM(require("path"));
var import_socketConnection = require("../utils/socketConnection");
var import_registry = require("./registry");
class Session {
  constructor(sessionFile) {
    this.config = sessionFile.config;
    this.name = this.config.name;
    this._sessionFile = sessionFile;
  }
  isCompatible(clientInfo) {
    return (0, import_socketConnection.compareSemver)(clientInfo.version, this.config.version) >= 0;
  }
  async run(clientInfo, args) {
    if (!this.isCompatible(clientInfo))
      throw new Error(`Client is v${clientInfo.version}, session '${this.name}' is v${this.config.version}. Run

  playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open

to restart the browser session.`);
    const { socket } = await this._connect();
    if (!socket)
      throw new Error(`Browser '${this.name}' is not open. Run

  playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open

to start the browser session.`);
    return await SocketConnectionClient.sendAndClose(socket, "run", { args, cwd: process.cwd() });
  }
  async stop(quiet = false) {
    if (!await this.canConnect()) {
      if (!quiet)
        console.log(`Browser '${this.name}' is not open.`);
      return;
    }
    await this._stopDaemon();
    if (!quiet)
      console.log(`Browser '${this.name}' closed
`);
  }
  async deleteData() {
    await this.stop();
    const dataDirs = await import_fs.default.promises.readdir(this._sessionFile.daemonDir).catch(() => []);
    const matchingEntries = dataDirs.filter((file) => file === `${this.name}.session` || file.startsWith(`ud-${this.name}-`));
    if (matchingEntries.length === 0) {
      console.log(`No user data found for browser '${this.name}'.`);
      return;
    }
    for (const entry of matchingEntries) {
      const userDataDir = import_path.default.resolve(this._sessionFile.daemonDir, entry);
      for (let i = 0; i < 5; i++) {
        try {
          await import_fs.default.promises.rm(userDataDir, { recursive: true });
          if (entry.startsWith("ud-"))
            console.log(`Deleted user data for browser '${this.name}'.`);
          break;
        } catch (e) {
          if (e.code === "ENOENT") {
            console.log(`No user data found for browser '${this.name}'.`);
            break;
          }
          await new Promise((resolve) => setTimeout(resolve, 1e3));
          if (i === 4)
            throw e;
        }
      }
    }
  }
  async _connect() {
    return await new Promise((resolve) => {
      const socket = import_net.default.createConnection(this.config.socketPath, () => {
        resolve({ socket });
      });
      socket.on("error", (error) => {
        if (import_os.default.platform() !== "win32")
          void import_fs.default.promises.unlink(this.config.socketPath).catch(() => {
          }).then(() => resolve({ error }));
        else
          resolve({ error });
      });
    });
  }
  async canConnect() {
    const { socket } = await this._connect();
    if (socket) {
      socket.destroy();
      return true;
    }
    return false;
  }
  static async startDaemon(clientInfo, cliArgs) {
    await import_fs.default.promises.mkdir(clientInfo.daemonProfilesDir, { recursive: true });
    const cliPath = require.resolve("../cli-daemon/program.js");
    const sessionName = (0, import_registry.resolveSessionName)(cliArgs.session);
    const errLog = import_path.default.join(clientInfo.daemonProfilesDir, sessionName + ".err");
    const err = import_fs.default.openSync(errLog, "w");
    const args = [
      cliPath,
      sessionName
    ];
    if (cliArgs.headed)
      args.push("--headed");
    if (cliArgs.extension)
      args.push("--extension");
    if (cliArgs.browser)
      args.push(`--browser=${cliArgs.browser}`);
    if (cliArgs.persistent)
      args.push("--persistent");
    if (cliArgs.profile)
      args.push(`--profile=${cliArgs.profile}`);
    if (cliArgs.config)
      args.push(`--config=${cliArgs.config}`);
    if (cliArgs.endpoint || process.env.PLAYWRIGHT_CLI_SESSION)
      args.push(`--endpoint=${cliArgs.endpoint || process.env.PLAYWRIGHT_CLI_SESSION}`);
    const child = (0, import_child_process.spawn)(process.execPath, args, {
      detached: true,
      stdio: ["ignore", "pipe", err],
      cwd: process.cwd()
      // Will be used as root.
    });
    let signalled = false;
    const sigintHandler = () => {
      signalled = true;
      child.kill("SIGINT");
    };
    const sigtermHandler = () => {
      signalled = true;
      child.kill("SIGTERM");
    };
    process.on("SIGINT", sigintHandler);
    process.on("SIGTERM", sigtermHandler);
    let outLog = "";
    await new Promise((resolve, reject) => {
      child.stdout.on("data", (data) => {
        outLog += data.toString();
        if (!outLog.includes("<EOF>"))
          return;
        const errorMatch = outLog.match(/### Error\n([\s\S]*)<EOF>/);
        const error = errorMatch ? errorMatch[1].trim() : void 0;
        if (error) {
          const errLogContent = import_fs.default.readFileSync(errLog, "utf-8");
          const message = error + (errLogContent ? "\n" + errLogContent : "");
          reject(new Error(message));
        }
        const successMatch = outLog.match(/### Success\nDaemon listening on (.*)\n<EOF>/);
        if (successMatch)
          resolve();
      });
      child.on("close", (code) => {
        if (!signalled) {
          const errLogContent = import_fs.default.readFileSync(errLog, "utf-8");
          const message = `Daemon process exited with code ${code}` + (errLogContent ? "\n" + errLogContent : "");
          reject(new Error(message));
        }
      });
    });
    process.off("SIGINT", sigintHandler);
    process.off("SIGTERM", sigtermHandler);
    child.stdout.destroy();
    child.unref();
    if (cliArgs["endpoint"]) {
      console.log(`### Session \`${sessionName}\` created, attached to \`${cliArgs["endpoint"]}\`.`);
      console.log(`Run commands with: playwright-cli --session=${sessionName} <command>`);
    } else {
      console.log(`### Browser \`${sessionName}\` opened with pid ${child.pid}.`);
    }
  }
  async _stopDaemon() {
    const { socket, error: socketError } = await this._connect();
    if (!socket) {
      console.log(`Browser '${this.name}' is not open.${socketError ? " Error: " + socketError.message : ""}`);
      return;
    }
    let error;
    await SocketConnectionClient.sendAndClose(socket, "stop", {}).catch((e) => error = e);
    if (error && !error?.message?.includes("Session closed"))
      throw error;
  }
  async deleteSessionConfig() {
    await import_fs.default.promises.rm(this._sessionFile.file).catch(() => {
    });
  }
}
function renderResolvedConfig(config) {
  const channel = config.browser.launchOptions.channel ?? config.browser.browserName;
  const lines = [];
  if (channel)
    lines.push(`  - browser-type: ${channel}`);
  if (!config.cli.persistent)
    lines.push(`  - user-data-dir: <in-memory>`);
  else
    lines.push(`  - user-data-dir: ${config.browser.userDataDir}`);
  lines.push(`  - headed: ${!config.browser.launchOptions.headless}`);
  return lines;
}
class SocketConnectionClient {
  constructor(socket) {
    this._nextMessageId = 1;
    this._callbacks = /* @__PURE__ */ new Map();
    this._connection = new import_socketConnection.SocketConnection(socket);
    this._connection.onmessage = (message) => this._onMessage(message);
    this._connection.onclose = () => this._rejectCallbacks();
  }
  async send(method, params = {}) {
    const messageId = this._nextMessageId++;
    const message = {
      id: messageId,
      method,
      params
    };
    const responsePromise = new Promise((resolve, reject) => {
      this._callbacks.set(messageId, { resolve, reject, method, params });
    });
    const [result] = await Promise.all([responsePromise, this._connection.send(message)]);
    return result;
  }
  static async sendAndClose(socket, method, params = {}) {
    const connection = new SocketConnectionClient(socket);
    try {
      return await connection.send(method, params);
    } finally {
      connection.close();
    }
  }
  close() {
    this._connection.close();
  }
  _onMessage(object) {
    if (object.id && this._callbacks.has(object.id)) {
      const callback = this._callbacks.get(object.id);
      this._callbacks.delete(object.id);
      if (object.error)
        callback.reject(new Error(object.error));
      else
        callback.resolve(object.result);
    } else if (object.id) {
      throw new Error(`Unexpected message id: ${object.id}`);
    } else {
      throw new Error(`Unexpected message without id: ${JSON.stringify(object)}`);
    }
  }
  _rejectCallbacks() {
    for (const callback of this._callbacks.values())
      callback.reject(new Error("Session closed"));
    this._callbacks.clear();
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  Session,
  renderResolvedConfig
});
