// Package netiputil provides compact binary encoding for IP prefixes used in
// the management proto wire format.
//
// Format: [IP bytes][1 byte prefix_len]
//   - IPv4: 5 bytes total (4 IP + 1 prefix_len, 0-32)
//   - IPv6: 17 bytes total (16 IP + 1 prefix_len, 0-128)
//
// Address family is determined by length: 5 = v4, 17 = v6.
package netiputil

import (
	"fmt"
	"net/netip"
)

// EncodePrefix encodes a netip.Prefix into compact bytes.
// The address is always unmapped before encoding.
func EncodePrefix(p netip.Prefix) ([]byte, error) {
	addr := p.Addr().Unmap()
	bits := p.Bits()
	if addr.Is4() && bits > 32 {
		return nil, fmt.Errorf("invalid prefix length %d for IPv4 address %s (max 32)", bits, addr)
	}
	return append(addr.AsSlice(), byte(bits)), nil
}

// DecodePrefix decodes compact bytes into a netip.Prefix.
func DecodePrefix(b []byte) (netip.Prefix, error) {
	switch len(b) {
	case 5:
		var ip4 [4]byte
		copy(ip4[:], b)
		bits := int(b[len(b)-1])
		if bits > 32 {
			return netip.Prefix{}, fmt.Errorf("invalid IPv4 prefix length %d (max 32)", bits)
		}
		return netip.PrefixFrom(netip.AddrFrom4(ip4), bits), nil
	case 17:
		var ip6 [16]byte
		copy(ip6[:], b)
		addr := netip.AddrFrom16(ip6).Unmap()
		bits := int(b[len(b)-1])
		if addr.Is4() {
			if bits > 32 {
				return netip.Prefix{}, fmt.Errorf("invalid prefix length %d for v4-mapped address (max 32)", bits)
			}
		} else if bits > 128 {
			return netip.Prefix{}, fmt.Errorf("invalid IPv6 prefix length %d (max 128)", bits)
		}
		return netip.PrefixFrom(addr, bits), nil
	default:
		return netip.Prefix{}, fmt.Errorf("invalid compact prefix length %d (expected 5 or 17)", len(b))
	}
}

// EncodeAddr encodes a netip.Addr into compact prefix bytes with a host prefix
// length (/32 for v4, /128 for v6). The address is always unmapped before encoding.
func EncodeAddr(a netip.Addr) []byte {
	a = a.Unmap()
	bits := 128
	if a.Is4() {
		bits = 32
	}
	// Host prefix lengths are always valid for the address family, so error is impossible.
	b, _ := EncodePrefix(netip.PrefixFrom(a, bits))
	return b
}

// DecodeAddr decodes compact prefix bytes and returns only the address,
// discarding the prefix length. Useful when the prefix length is implied
// (e.g. peer overlay IPs are always /32 or /128).
func DecodeAddr(b []byte) (netip.Addr, error) {
	p, err := DecodePrefix(b)
	if err != nil {
		return netip.Addr{}, err
	}
	return p.Addr(), nil
}
