"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var tunnel_utils_exports = {};
__export(tunnel_utils_exports, {
  ReconnectionFailedError: () => ReconnectionFailedError,
  TunnelSupervisor: () => TunnelSupervisor
});
module.exports = __toCommonJS(tunnel_utils_exports);
var import_tunnel = require("@bpinternal/tunnel");
var import_event_emitter = require("./event-emitter");
class ReconnectionFailedError extends Error {
  constructor(event) {
    const reason = ReconnectionFailedError._reason(event);
    super(`Reconnection failed: ${reason}`);
    this.event = event;
  }
  static _reason(event) {
    if (event.type === "error") {
      return "error";
    }
    if (event.type === "close") {
      return `${event.ev.code} ${event.ev.reason}`;
    }
    return "init";
  }
}
class TunnelSupervisor {
  constructor(_tunnelUrl, _tunnelId, _logger) {
    this._tunnelUrl = _tunnelUrl;
    this._tunnelId = _tunnelId;
    this._logger = _logger;
  }
  _tunnel;
  _closed = false;
  _started = false;
  events = new import_event_emitter.EventEmitter();
  async start() {
    if (this._closed) {
      throw new Error("Cannot start: Tunnel is closed");
    }
    if (this._started) {
      throw new Error("Cannot start: Tunnel is already started");
    }
    this._started = true;
    const tunnel = await this._reconnect({ type: "init", ev: null });
    this._tunnel = tunnel;
  }
  get closed() {
    return this._closed;
  }
  /**
   * @returns Promise that rejects when a reconnection attempt fails and resolves when the tunnel is closed manually
   */
  async wait() {
    if (this._closed) {
      throw new Error("Cannot wait: Tunnel is closed");
    }
    return new Promise((resolve, reject) => {
      this.events.on("connectionFailed", (ev) => {
        reject(new ReconnectionFailedError(ev));
      });
      this.events.on("manuallyClosed", () => {
        resolve();
      });
    });
  }
  close() {
    if (this._closed) {
      return;
    }
    this._closed = true;
    this._tunnel?.close();
    this.events.emit("manuallyClosed", null);
  }
  _reconnectSync(ev) {
    void this._reconnect(ev).then((t) => {
      this._tunnel = t;
    }).catch(() => this.events.emit("connectionFailed", ev));
  }
  async _reconnect(ev) {
    const newTunnel = async () => {
      const tunnel2 = await import_tunnel.TunnelTail.new(this._tunnelUrl, this._tunnelId);
      this._registerListeners(tunnel2);
      this.events.emit("connected", { tunnel: tunnel2, ev });
      return tunnel2;
    };
    if (ev.type === "init") {
      return newTunnel();
    }
    const line = this._logger.line();
    line.started("Reconnecting tunnel...");
    const tunnel = await newTunnel();
    line.success("Reconnected");
    line.commit();
    return tunnel;
  }
  _registerListeners(tunnel) {
    tunnel.events.on("error", ({ target, type }) => {
      this._logger.error(`Tunnel error: ${type}`);
      this._reconnectSync({ type: "error", ev: { target, type } });
    });
    tunnel.events.on("close", ({ code, reason, target, type, wasClean }) => {
      this._logger.error(`Tunnel closed: ${code} ${reason}`);
      if (code === import_tunnel.errors.CLOSE_CODES.TUNNEL_ID_CONFLICT) {
        throw new Error("Cannot start: Tunnel Id is already used, choose a different tunnel id.");
      }
      this._reconnectSync({ type: "close", ev: { code, reason, target, type, wasClean } });
    });
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  ReconnectionFailedError,
  TunnelSupervisor
});
//# sourceMappingURL=tunnel-utils.js.map
