import {
  logger
} from "./chunk-K44MW7JJ.js";

// src/server/dev-server/inspect-proxy.ts
import net from "net";
import zlib from "zlib";
var OPENFOX_BASE_PORT = Number(process.env["OPENFOX_PORT"] ?? 10369);
var INJECT_SCRIPT = `<script src="http://127.0.0.1:${OPENFOX_BASE_PORT}/__inspect__.js"></script>`;
function buildHttpHeaders(resHeaders, status, contentLengthOverride, contentTypeOverride) {
  const newH = { ...resHeaders };
  delete newH["content-length"];
  delete newH["transfer-encoding"];
  newH["connection"] = "close";
  if (contentLengthOverride !== void 0) newH["content-length"] = contentLengthOverride.toString();
  if (contentTypeOverride !== void 0) newH["content-type"] = contentTypeOverride;
  return `HTTP/1.1 ${status} OK\r
` + Object.entries(newH).map(([k, v]) => `${k}: ${v}`).join("\r\n") + "\r\n\r\n";
}
var proxyPool = /* @__PURE__ */ new Map();
var nextOffset = 0;
function getAvailablePort() {
  const base = Number(process.env["OPENFOX_PORT"] ?? 10369);
  const used = /* @__PURE__ */ new Set();
  for (const instance of proxyPool.values()) {
    const addr = instance.server.address();
    if (addr && typeof addr === "object") used.add(addr.port);
  }
  for (let port = base + 1; port < base + 200; port++) {
    if (!used.has(port)) return port;
  }
  return base + (nextOffset++ % 200 + 1);
}
function parseReqHeaders(str) {
  const lines = str.split("\r\n");
  const method = lines[0].split(" ")[0];
  const url = lines[0].split(" ")[1];
  const headers = parseHeaderLines(lines);
  return { method, url, headers };
}
function parseResHeaders(str) {
  const lines = str.split("\r\n");
  const status = parseInt(lines[0].split(" ")[1] || "200");
  const headers = parseHeaderLines(lines);
  return { status, headers };
}
function parseHeaderLines(lines) {
  const headers = {};
  for (let i = 1; i < lines.length; i++) {
    const line = lines[i];
    if (!line) break;
    const ci = line.indexOf(":");
    if (ci < 0) continue;
    headers[line.slice(0, ci).toLowerCase()] = line.slice(ci + 1).trim();
  }
  return headers;
}
function buildResponse(status, headers, body) {
  const statusLine = `HTTP/1.1 ${status} ${status === 200 ? "OK" : status === 404 ? "Not Found" : "Error"}`;
  const headerLines = Object.entries(headers).map(([k, v]) => `${k}: ${v}`).join("\r\n");
  const head = Buffer.from(`${statusLine}\r
${headerLines}\r
\r
`, "utf8");
  return Buffer.concat([head, body]);
}
function dechunk(buf) {
  const str = buf.toString("utf8");
  const parts = [];
  let pos = 0;
  while (pos < str.length) {
    const nlIdx = str.indexOf("\r\n", pos);
    if (nlIdx < 0) break;
    const sizeStr = str.slice(pos, nlIdx);
    const size = parseInt(sizeStr, 16);
    if (isNaN(size) || size < 0) break;
    if (size === 0) break;
    const chunkStart = nlIdx + 2;
    const chunkEnd = chunkStart + size;
    if (chunkEnd > str.length) break;
    parts.push(str.slice(chunkStart, chunkEnd));
    pos = chunkEnd + 2;
  }
  return Buffer.from(parts.join(""), "utf8");
}
function startInspectProxy(target, sessionManager) {
  const port = getAvailablePort();
  const server = net.createServer((client) => {
    let clientHead = "";
    let clientParsed = false;
    let targetSocket;
    client.on("data", (chunk) => {
      if (clientParsed) {
        targetSocket?.write(chunk);
        return;
      }
      clientHead += chunk.toString("utf8");
      const he = clientHead.indexOf("\r\n\r\n");
      if (he < 0) return;
      const { method, url, headers } = parseReqHeaders(clientHead);
      const isWS = headers["upgrade"] === "websocket";
      clientParsed = true;
      if (url === "/__openfox_feedback" && method === "POST") {
        const contentLength = parseInt(headers["content-length"] || "0", 10);
        let bodyData = chunk.slice(he + 4);
        let bodyTotal = bodyData.length;
        const handleFeedback = () => {
          try {
            const { sessionId, element, annotation, pageUrl } = JSON.parse(bodyData.toString("utf8"));
            if (sessionId) {
              const elementDesc = element ? `${element.tag}${element.id ? "#" + element.id : ""}` : "unknown";
              const htmlSnippet = element?.outerHTML ? `
Html: ${element.outerHTML.slice(0, 300)}` : "";
              const textSnippet = element?.textContent ? `
Text (SVG-stripped): ${element.textContent.slice(0, 500)}` : "";
              const content = `# User feedback from page inspection on dev_server

## Context

Page: ${pageUrl || ""}
Element: ${elementDesc}
xPath: ${element?.xpath || ""}${htmlSnippet}${textSnippet}

## Feedback

${annotation || "(none)"}`;
              sessionManager.queueMessage(sessionId, "asap", content, [], "ui_feedback");
            }
            client.write(buildResponse(200, { "Content-Type": "application/json" }, Buffer.from('{"success":true}')));
          } catch {
            client.write(
              buildResponse(400, { "Content-Type": "application/json" }, Buffer.from('{"error":"Invalid request"}'))
            );
          }
          client.end();
        };
        if (bodyTotal >= contentLength) {
          handleFeedback();
          return;
        }
        client.on("data", (more) => {
          bodyData = Buffer.concat([bodyData, more]);
          bodyTotal += more.length;
          if (bodyTotal < contentLength) return;
          handleFeedback();
        });
        return;
      }
      const targetParts = target.replace(/^https?:\/\//, "").split(":");
      const targetHost = targetParts[0] ?? "127.0.0.1";
      const targetPort = parseInt(targetParts[1] ?? "80");
      if (isWS) {
        targetSocket = net.connect(targetPort, targetHost);
        targetSocket.on("error", () => client.destroy());
        client.on("error", () => targetSocket.destroy());
        targetSocket.write(clientHead);
        targetSocket.pipe(client);
        return;
      }
      targetSocket = net.connect(targetPort, targetHost);
      targetSocket.on("error", () => client.destroy());
      client.on("error", () => targetSocket.destroy());
      client.on("end", () => targetSocket.end());
      const connClose = "\r\nConnection: close";
      const reqEnd = clientHead.indexOf("\r\n\r\n");
      const forwardHead = clientHead.slice(0, reqEnd) + connClose + clientHead.slice(reqEnd);
      targetSocket.write(forwardHead);
      targetSocket.on("close", () => {
        if (!client.destroyed) client.end();
      });
      let serverHeadBuf = "";
      let serverParsed = false;
      let isHtml = false;
      let enc = null;
      let status = 200;
      let resHeaders = {};
      const bodyBuf = [];
      let headEnd = -1;
      targetSocket.on("data", (sChunk) => {
        if (!serverParsed) {
          serverHeadBuf += sChunk.toString("utf8");
          const sHe = serverHeadBuf.indexOf("\r\n\r\n");
          if (sHe < 0) return;
          headEnd = sHe + 4;
          const p = parseResHeaders(serverHeadBuf.slice(0, sHe));
          status = p.status;
          resHeaders = p.headers;
          isHtml = (resHeaders["content-type"] || "").includes("text/html");
          enc = resHeaders["content-encoding"] || null;
          const isChunked = (resHeaders["transfer-encoding"] || "").toLowerCase() === "chunked";
          serverParsed = true;
          if (isHtml && enc) {
            bodyBuf.push(sChunk.slice(headEnd));
            return;
          }
          if (isHtml && isChunked) {
            bodyBuf.push(sChunk.slice(headEnd));
            return;
          }
          if (isHtml) {
            bodyBuf.push(sChunk.slice(headEnd));
            return;
          }
          client.write(sChunk);
          return;
        }
        if (isHtml && enc) {
          bodyBuf.push(sChunk);
          return;
        }
        if (isHtml) {
          bodyBuf.push(sChunk);
          return;
        }
        client.write(sChunk);
      });
      targetSocket.on("end", () => {
        if (!serverParsed) {
          client.end();
          return;
        }
        if (isHtml && enc) {
          const fullBody = Buffer.concat(bodyBuf);
          let text;
          try {
            if (enc === "gzip") text = zlib.gunzipSync(fullBody).toString("utf8");
            else if (enc === "deflate") text = zlib.inflateSync(fullBody).toString("utf8");
            else text = fullBody.toString("utf8");
          } catch {
            client.end();
            return;
          }
          const bi = text.indexOf("</body>");
          const hi = text.indexOf("</head>");
          let modified;
          if (bi >= 0) modified = text.slice(0, bi) + INJECT_SCRIPT + text.slice(bi);
          else if (hi >= 0) modified = text.slice(0, hi) + INJECT_SCRIPT + text.slice(hi);
          else {
            client.end();
            return;
          }
          let compressed;
          if (enc === "gzip") compressed = zlib.gzipSync(Buffer.from(modified, "utf8"));
          else if (enc === "deflate") compressed = zlib.deflateSync(Buffer.from(modified, "utf8"));
          else compressed = Buffer.from(modified, "utf8");
          const headStr = buildHttpHeaders(resHeaders, status);
          client.write(Buffer.from(headStr, "utf8"));
          client.write(compressed);
        } else if (isHtml && bodyBuf.length > 0) {
          const fullBody = Buffer.concat(bodyBuf);
          const isChunked = (resHeaders["transfer-encoding"] || "").toLowerCase() === "chunked";
          const dechunks = isChunked ? dechunk(fullBody) : fullBody;
          const bi = dechunks.indexOf("</body>");
          const hi = dechunks.indexOf("</head>");
          let modified;
          if (bi >= 0)
            modified = Buffer.concat([dechunks.slice(0, bi), Buffer.from(INJECT_SCRIPT, "utf8"), dechunks.slice(bi)]);
          else if (hi >= 0)
            modified = Buffer.concat([dechunks.slice(0, hi), Buffer.from(INJECT_SCRIPT, "utf8"), dechunks.slice(hi)]);
          else {
            client.end();
            return;
          }
          const headStr = buildHttpHeaders(resHeaders, status, Buffer.byteLength(modified), "text/html; charset=utf-8");
          client.write(Buffer.from(headStr, "utf8"));
          client.write(modified);
        }
        client.end();
      });
    });
    client.on("error", () => {
    });
  });
  server.listen(port, "0.0.0.0", () => {
    logger.debug("Inspect proxy listening", { port, target });
  });
  proxyPool.set(target, { server, target });
  const cleanup = () => {
    server.close();
    proxyPool.delete(target);
    logger.debug("Inspect proxy stopped", { port, target });
  };
  return { port, cleanup };
}
function stopAllInspectProxies() {
  for (const instance of proxyPool.values()) {
    instance.server.close();
  }
  proxyPool.clear();
}

export {
  startInspectProxy,
  stopAllInspectProxies
};
//# sourceMappingURL=chunk-DL6ZILAF.js.map