export interface OverlayPeerRef {
  serverId: number
  address: string
  allowedIps: string[]
}

export function normalizeExporterIp(ip: string): string {
  const trimmed = ip.trim()
  if (trimmed.toLowerCase().startsWith("::ffff:")) return trimmed.slice(7)
  return trimmed
}

export function bareIpv4(value: string): string {
  const raw = normalizeExporterIp(value).split("/")[0]?.trim() ?? ""
  return raw
}

function ipv4ToInt(ip: string): number | null {
  const parts = ip.split(".")
  if (parts.length !== 4) return null
  const n = parts.map((x) => Number(x))
  if (n.some((x) => !Number.isInteger(x) || x < 0 || x > 255)) return null
  return ((n[0]! << 24) | (n[1]! << 16) | (n[2]! << 8) | n[3]!) >>> 0
}

export function ipInCidr(ip: string, cidr: string): boolean {
  const host = bareIpv4(ip)
  const [base, bitsRaw] = cidr.split("/")
  const bits = Number(bitsRaw ?? 32)
  const a = ipv4ToInt(host)
  const b = ipv4ToInt(bareIpv4(base ?? ""))
  if (a == null || b == null || !Number.isFinite(bits) || bits < 0 || bits > 32) return false
  const mask = bits === 0 ? 0 : (0xffffffff << (32 - bits)) >>> 0
  return (a & mask) === (b & mask)
}

/** Docker userland-proxy / bridge SNAT, не адрес из оверлея wg-flow. */
export function isNatMasqueradeExporter(ip: string, overlayPrefix: string): boolean {
  const host = bareIpv4(ip)
  if (!host) return false
  if (ipInCidr(host, overlayPrefix)) return false
  return ipInCidr(host, "10.0.0.0/8")
    || ipInCidr(host, "172.16.0.0/12")
    || ipInCidr(host, "192.168.0.0/16")
    || ipInCidr(host, "127.0.0.0/8")
}

export function pickServerIdForExporter(opts: {
  exporterIp: string
  overlayPrefix: string
  byTunnelIp: Map<string, number>
  peers: OverlayPeerRef[]
  hostIps: Map<string, number>
}): number | null {
  const exporter = bareIpv4(opts.exporterIp)
  if (!exporter) return null

  const exact = opts.byTunnelIp.get(exporter)
  if (exact != null) return exact

  for (const [ip, id] of opts.byTunnelIp) {
    if (bareIpv4(ip) === exporter) return id
  }

  for (const peer of opts.peers) {
    if (bareIpv4(peer.address) === exporter) return peer.serverId
    if (peer.allowedIps.some((cidr) => ipInCidr(exporter, cidr) || bareIpv4(cidr) === exporter)) {
      return peer.serverId
    }
  }

  const byHost = opts.hostIps.get(exporter)
  if (byHost != null) return byHost

  if (!isNatMasqueradeExporter(exporter, opts.overlayPrefix)) return null

  const tunnelIds = [...new Set(opts.byTunnelIp.values())]
  if (tunnelIds.length === 1) return tunnelIds[0] ?? null
  const peerIds = [...new Set(opts.peers.map((p) => p.serverId))]
  if (peerIds.length === 1) return peerIds[0] ?? null
  return null
}
