"use strict";
/* Socket Lib - Built with esbuild */
"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 request_attempt_exports = {};
__export(request_attempt_exports, {
  httpRequestAttempt: () => httpRequestAttempt
});
module.exports = __toCommonJS(request_attempt_exports);
var import_date = require("../primordials/date");
var import_error = require("../primordials/error");
var import_json = require("../primordials/json");
var import_object = require("../primordials/object");
var import_promise = require("../primordials/promise");
var import_url = require("../primordials/url");
var import_internal = require("./_internal");
var import_errors = require("./errors");
var import_user_agent = require("./user-agent");
async function httpRequestAttempt(url, options) {
  const {
    body,
    ca,
    followRedirects = true,
    headers = {},
    hooks,
    maxRedirects = 5,
    maxResponseSize,
    method = "GET",
    stream = false,
    timeout = 3e4
  } = { __proto__: null, ...options };
  const startTime = (0, import_date.DateNow)();
  const streamHeaders = body && typeof body === "object" && "getHeaders" in body && typeof body.getHeaders === "function" ? body.getHeaders() : void 0;
  const mergedHeaders = {
    "User-Agent": (0, import_user_agent.getSocketCallerUserAgent)(),
    ...streamHeaders,
    ...headers
  };
  hooks?.onRequest?.({ method, url, headers: mergedHeaders, timeout });
  return await new import_promise.PromiseCtor((resolve, reject) => {
    let settled = false;
    const resolveOnce = (response) => {
      if (settled) {
        return;
      }
      settled = true;
      resolve(response);
    };
    const rejectOnce = (err) => {
      if (settled) {
        return;
      }
      settled = true;
      if (body && typeof body === "object" && typeof body.destroy === "function") {
        ;
        body.destroy();
      }
      emitResponse({ error: err });
      reject(err);
    };
    const parsedUrl = new import_url.URLCtor(url);
    const isHttps = parsedUrl.protocol === "https:";
    const httpModule = isHttps ? (0, import_internal.getHttps)() : (0, import_internal.getHttp)();
    const requestOptions = {
      headers: mergedHeaders,
      hostname: parsedUrl.hostname,
      method,
      path: parsedUrl.pathname + parsedUrl.search,
      port: parsedUrl.port,
      timeout
    };
    if (ca && isHttps) {
      requestOptions["ca"] = ca;
    }
    const emitResponse = (info) => {
      try {
        hooks?.onResponse?.({
          duration: (0, import_date.DateNow)() - startTime,
          method,
          url,
          ...info
        });
      } catch {
      }
    };
    const request = httpModule.request(
      requestOptions,
      (res) => {
        if (followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
          res.resume();
          emitResponse({
            headers: res.headers,
            status: res.statusCode,
            statusText: res.statusMessage
          });
          if (maxRedirects <= 0) {
            settled = true;
            reject(
              new import_error.ErrorCtor(
                `Too many redirects (exceeded maximum: ${maxRedirects})`
              )
            );
            return;
          }
          const redirectUrl = res.headers.location.startsWith("http") ? res.headers.location : new URL(res.headers.location, url).toString();
          const redirectParsed = new URL(redirectUrl);
          if (isHttps && redirectParsed.protocol !== "https:") {
            settled = true;
            reject(
              new import_error.ErrorCtor(
                `Redirect from HTTPS to HTTP is not allowed: ${redirectUrl}`
              )
            );
            return;
          }
          let redirectHeaders = headers;
          if (new URL(url).origin !== redirectParsed.origin) {
            redirectHeaders = { __proto__: null };
            const stripped = /* @__PURE__ */ new Set([
              "authorization",
              "cookie",
              "proxy-authenticate",
              "proxy-authorization"
            ]);
            for (const key of (0, import_object.ObjectKeys)(headers)) {
              if (!stripped.has(key.toLowerCase())) {
                ;
                redirectHeaders[key] = headers[key];
              }
            }
          }
          settled = true;
          resolve(
            httpRequestAttempt(redirectUrl, {
              body,
              ca,
              followRedirects,
              headers: redirectHeaders,
              hooks,
              maxRedirects: maxRedirects - 1,
              maxResponseSize,
              method,
              stream,
              timeout
            })
          );
          return;
        }
        if (stream) {
          const status = res.statusCode || 0;
          const statusText = res.statusMessage || "";
          const ok = status >= 200 && status < 300;
          emitResponse({
            headers: res.headers,
            status,
            statusText
          });
          const emptyBody = Buffer.alloc(0);
          resolveOnce({
            arrayBuffer: () => emptyBody.buffer,
            body: emptyBody,
            headers: res.headers,
            json: () => {
              throw new import_error.ErrorCtor("Cannot parse JSON from a streaming response");
            },
            ok,
            rawResponse: res,
            status,
            statusText,
            text: () => ""
          });
          return;
        }
        const chunks = [];
        let totalBytes = 0;
        res.on("data", (chunk) => {
          totalBytes += chunk.length;
          if (maxResponseSize && totalBytes > maxResponseSize) {
            res.destroy();
            request.destroy();
            const sizeMB = (totalBytes / (1024 * 1024)).toFixed(2);
            const maxMB = (maxResponseSize / (1024 * 1024)).toFixed(2);
            rejectOnce(
              new import_error.ErrorCtor(
                `Response exceeds maximum size limit (${sizeMB}MB > ${maxMB}MB)`
              )
            );
            return;
          }
          chunks.push(chunk);
        });
        res.on("end", () => {
          if (settled) {
            return;
          }
          const responseBody = Buffer.concat(chunks);
          const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
          const response = {
            arrayBuffer() {
              return responseBody.buffer.slice(
                responseBody.byteOffset,
                responseBody.byteOffset + responseBody.byteLength
              );
            },
            body: responseBody,
            headers: res.headers,
            json() {
              return (0, import_json.JSONParse)(responseBody.toString("utf8"));
            },
            ok,
            rawResponse: res,
            status: res.statusCode || 0,
            statusText: res.statusMessage || "",
            text() {
              return responseBody.toString("utf8");
            }
          };
          emitResponse({
            headers: res.headers,
            status: res.statusCode,
            statusText: res.statusMessage
          });
          resolveOnce(response);
        });
        res.on("error", (error) => {
          rejectOnce(error);
        });
      }
    );
    request.on("error", (error) => {
      const message = (0, import_errors.enrichErrorMessage)(
        url,
        method,
        error
      );
      rejectOnce(new import_error.ErrorCtor(message, { cause: error }));
    });
    request.on("timeout", () => {
      request.destroy();
      rejectOnce(
        new import_error.ErrorCtor(
          `${method} request timed out after ${timeout}ms: ${url}
\u2192 Server did not respond in time.
\u2192 Try: Increase timeout or check network connectivity.`
        )
      );
    });
    if (body) {
      if (typeof body === "object" && typeof body.pipe === "function") {
        const stream2 = body;
        stream2.on("error", (err) => {
          request.destroy();
          rejectOnce(err);
        });
        stream2.pipe(request);
        return;
      }
      request.write(body);
      request.end();
    } else {
      request.end();
    }
  });
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  httpRequestAttempt
});
