import { M as Message, P as Peer, t as toBufferLike } from '../shared/crossws.DfCzGthR.mjs';
import { A as AdapterHookable, a as adapterUtils } from '../shared/crossws.D9ehKjSh.mjs';
import { W as WSError } from '../shared/crossws.By9qWDAI.mjs';
import { _ as _WebSocketServer } from '../shared/crossws.CipVM6lf.mjs';
import 'uncrypto';
import 'stream';
import 'events';
import 'http';
import 'crypto';
import 'buffer';
import 'zlib';
import 'https';
import 'net';
import 'tls';
import 'url';

const nodeAdapter = (options = {}) => {
  const hooks = new AdapterHookable(options);
  const peers = /* @__PURE__ */ new Set();
  const wss = options.wss || new _WebSocketServer({
    noServer: true,
    ...options.serverOptions
  });
  wss.on("connection", (ws, nodeReq) => {
    const request = new NodeReqProxy(nodeReq);
    const peer = new NodePeer({ ws, request, peers, nodeReq });
    peers.add(peer);
    hooks.callHook("open", peer);
    ws.on("message", (data) => {
      if (Array.isArray(data)) {
        data = Buffer.concat(data);
      }
      hooks.callHook("message", peer, new Message(data, peer));
    });
    ws.on("error", (error) => {
      peers.delete(peer);
      hooks.callHook("error", peer, new WSError(error));
    });
    ws.on("close", (code, reason) => {
      peers.delete(peer);
      hooks.callHook("close", peer, {
        code,
        reason: reason?.toString()
      });
    });
  });
  wss.on("headers", (outgoingHeaders, req) => {
    const upgradeHeaders = req._upgradeHeaders;
    if (upgradeHeaders) {
      for (const [key, value] of new Headers(upgradeHeaders)) {
        outgoingHeaders.push(`${key}: ${value}`);
      }
    }
  });
  return {
    ...adapterUtils(peers),
    handleUpgrade: async (nodeReq, socket, head) => {
      const request = new NodeReqProxy(nodeReq);
      const { upgradeHeaders, endResponse, context } = await hooks.upgrade(request);
      if (endResponse) {
        return sendResponse(socket, endResponse);
      }
      nodeReq._request = request;
      nodeReq._upgradeHeaders = upgradeHeaders;
      nodeReq._context = context;
      wss.handleUpgrade(nodeReq, socket, head, (ws) => {
        wss.emit("connection", ws, nodeReq);
      });
    },
    closeAll: (code, data, force) => {
      for (const client of wss.clients) {
        if (force) {
          client.terminate();
        } else {
          client.close(code, data);
        }
      }
    }
  };
};
class NodePeer extends Peer {
  get remoteAddress() {
    return this._internal.nodeReq.socket?.remoteAddress;
  }
  get context() {
    return this._internal.nodeReq._context;
  }
  send(data, options) {
    const dataBuff = toBufferLike(data);
    const isBinary = typeof dataBuff !== "string";
    this._internal.ws.send(dataBuff, {
      compress: options?.compress,
      binary: isBinary,
      ...options
    });
    return 0;
  }
  publish(topic, data, options) {
    const dataBuff = toBufferLike(data);
    const isBinary = typeof data !== "string";
    const sendOptions = {
      compress: options?.compress,
      binary: isBinary,
      ...options
    };
    for (const peer of this._internal.peers) {
      if (peer !== this && peer._topics.has(topic)) {
        peer._internal.ws.send(dataBuff, sendOptions);
      }
    }
  }
  close(code, data) {
    this._internal.ws.close(code, data);
  }
  terminate() {
    this._internal.ws.terminate();
  }
}
class NodeReqProxy {
  _req;
  _headers;
  _url;
  constructor(req) {
    this._req = req;
  }
  get url() {
    if (!this._url) {
      const req = this._req;
      const host = req.headers["host"] || "localhost";
      const isSecure = req.socket?.encrypted ?? req.headers["x-forwarded-proto"] === "https";
      this._url = `${isSecure ? "https" : "http"}://${host}${req.url}`;
    }
    return this._url;
  }
  get headers() {
    if (!this._headers) {
      this._headers = new Headers(this._req.headers);
    }
    return this._headers;
  }
}
async function sendResponse(socket, res) {
  const head = [
    `HTTP/1.1 ${res.status || 200} ${res.statusText || ""}`,
    ...[...res.headers.entries()].map(
      ([key, value]) => `${encodeURIComponent(key)}: ${encodeURIComponent(value)}`
    )
  ];
  socket.write(head.join("\r\n") + "\r\n\r\n");
  if (res.body) {
    for await (const chunk of res.body) {
      socket.write(chunk);
    }
  }
  return new Promise((resolve) => {
    socket.end(() => {
      socket.destroy();
      resolve();
    });
  });
}

export { nodeAdapter as default };
