// src/lib/normalize-rate-limit-ip.ts
function parseIpv4Dotted(s) {
  const p = s.split(".");
  if (p.length !== 4) {
    return null;
  }
  const b = new Uint8Array(4);
  for (let i = 0; i < 4; i++) {
    if (!/^\d{1,3}$/.test(p[i])) {
      return null;
    }
    const n = Number(p[i]);
    if (!Number.isInteger(n) || n < 0 || n > 255) {
      return null;
    }
    b[i] = n;
  }
  return b;
}
function ipv4Key(bytes) {
  return `4:${bytes[0]}.${bytes[1]}.${bytes[2]}.${bytes[3]}`;
}
function maskIpv6InPlace(bytes, prefixBits) {
  if (prefixBits >= 128) {
    return;
  }
  const whole = Math.floor(prefixBits / 8);
  const rem = prefixBits % 8;
  if (rem === 0) {
    for (let i = whole; i < 16; i++) {
      bytes[i] = 0;
    }
  } else {
    for (let i = whole + 1; i < 16; i++) {
      bytes[i] = 0;
    }
    const keep = 255 << 8 - rem & 255;
    bytes[whole] &= keep;
  }
}
function isIpv4MappedIpv6(bytes) {
  for (let i = 0; i < 10; i++) {
    if (bytes[i] !== 0) {
      return false;
    }
  }
  return bytes[10] === 255 && bytes[11] === 255;
}
function parseIpv6HextetsToBytes(s) {
  const buf = new Uint8Array(16);
  if (s === "") {
    return buf;
  }
  const double = s.includes("::");
  if (double && s.split("::").length > 2) {
    return null;
  }
  const [leftRaw, rightRaw] = double ? s.split("::", 2) : [s, ""];
  const left = leftRaw ? leftRaw.split(":").filter(Boolean) : [];
  const right = rightRaw ? rightRaw.split(":").filter(Boolean) : [];
  const partsLen = left.length + right.length;
  if (double) {
    if (partsLen > 8) {
      return null;
    }
  } else if (partsLen !== 8) {
    return null;
  }
  const pad = double ? 8 - partsLen : 0;
  const all = [...left, ...Array(pad).fill("0"), ...right];
  if (all.length !== 8) {
    return null;
  }
  for (let i = 0; i < 8; i++) {
    const h = all[i];
    if (!h || h.includes(".")) {
      return null;
    }
    const v = Number.parseInt(h, 16);
    if (v > 65535 || Number.isNaN(v)) {
      return null;
    }
    buf[i * 2] = v >> 8;
    buf[i * 2 + 1] = v & 255;
  }
  return buf;
}
function parseIpv6ToBytes(address) {
  let a = address.trim().toLowerCase();
  if (a.startsWith("[") && a.endsWith("]")) {
    a = a.slice(1, -1);
  }
  const zi = a.indexOf("%");
  if (zi >= 0) {
    a = a.slice(0, zi);
  }
  if (a === "") {
    return null;
  }
  if (!a.includes(":")) {
    const v4 = parseIpv4Dotted(a);
    return v4 ? v4 : null;
  }
  const lastColon = a.lastIndexOf(":");
  if (lastColon > 0) {
    const maybeV4 = a.slice(lastColon + 1);
    if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(maybeV4)) {
      const v4b = parseIpv4Dotted(maybeV4);
      if (!v4b) {
        return null;
      }
      const prefix = a.slice(0, lastColon);
      const w1 = v4b[0] << 8 | v4b[1];
      const w2 = v4b[2] << 8 | v4b[3];
      const tailHex = `${w1.toString(16)}:${w2.toString(16)}`;
      let merged;
      if (!prefix) {
        merged = tailHex;
      } else if (prefix.endsWith(":")) {
        merged = `${prefix}${tailHex}`;
      } else {
        merged = `${prefix}:${tailHex}`;
      }
      return parseIpv6HextetsToBytes(merged);
    }
  }
  return parseIpv6HextetsToBytes(a);
}
function bytesToHex(bytes) {
  let o = "";
  for (let i = 0; i < bytes.length; i++) {
    o += bytes[i].toString(16).padStart(2, "0");
  }
  return o;
}
function rateLimitClientKey(raw, ipv6SubnetBits) {
  const s = raw.trim();
  if (!s || s === "unknown") {
    return "unknown";
  }
  const prefixBits = Number.isFinite(ipv6SubnetBits) && ipv6SubnetBits >= 1 && ipv6SubnetBits <= 128 ? Math.floor(ipv6SubnetBits) : 64;
  if (!s.includes(":")) {
    const v4 = parseIpv4Dotted(s);
    return v4 ? ipv4Key(v4) : `raw:${s}`;
  }
  const bytes = parseIpv6ToBytes(s);
  if (!bytes) {
    return `raw:${s}`;
  }
  if (isIpv4MappedIpv6(bytes)) {
    return ipv4Key(bytes.subarray(12, 16));
  }
  maskIpv6InPlace(bytes, prefixBits);
  return `6:${bytesToHex(bytes)}`;
}
export {
  rateLimitClientKey
};
//# sourceMappingURL=normalize-rate-limit-ip.js.map