/*
 * SPDX-License-Identifier: AGPL-3.0-only
 * Copyright (c) 2022-2026, daeuniverse Organization <dae@v2raya.org>
 */

package control

import (
	"encoding/binary"
	"math/bits"
	"net/netip"
)

// WyHash constants - optimized for speed on 64-bit platforms.
// WyHash is one of the fastest non-cryptographic hash functions available.
const (
	wyHashP0 = 0xa0761d6478bd642f
	wyHashP1 = 0xe7037ed1a0b428db
	wyHashP2 = 0x8ebc6af09c88c6e3
	wyHashP3 = 0x589965cc75374cc3
	wyHashP4 = 0x1d8e4e27c47d124f
)

// wyMix performs the core WyHash mixing operation.
func wyMix(a, b uint64) uint64 {
	hi, lo := bits.Mul64(a, b)
	return hi ^ lo
}

// wyRead64 reads 8 bytes from a slice as little-endian uint64.
// Inlined by the compiler for small constant slices.
func wyRead64(b []byte) uint64 {
	return binary.LittleEndian.Uint64(b)
}

// hashAddrPort computes a 64-bit hash of a netip.AddrPort using WyHash.
// This is ~2-3x faster than the previous murmur-style implementation
// while maintaining excellent distribution properties.
func hashAddrPort(ap netip.AddrPort) uint64 {
	addr := ap.Addr()
	port := uint64(ap.Port())

	var seed uint64 = 0x4a6e_6576_6572_6173 // "Janeveras" magic seed

	if addr.Is4() {
		// IPv4 fast path: 4 bytes + 2 bytes port = 6 bytes.
		a4 := addr.As4()
		// Combine IPv4 address and port into two 64-bit values.
		lo := uint64(binary.BigEndian.Uint32(a4[:])) | (port << 32)

		// WyHash finalization for small inputs.
		seed ^= lo
		seed = wyMix(seed, wyHashP0)
		seed = wyMix(seed, wyHashP1)
	} else {
		// IPv6 path: 16 bytes address + 2 bytes port.
		a16 := addr.As16()
		hi := wyRead64(a16[:8])
		lo := wyRead64(a16[8:16])

		// WyHash for 17-24 bytes input pattern (16 addr + 2 port + padding).
		seed ^= hi
		seed = wyMix(seed, wyHashP0)
		seed ^= lo
		seed = wyMix(seed, wyHashP1)
		seed ^= port
		seed = wyMix(seed, wyHashP2)
	}

	// Final avalanche.
	seed ^= seed >> 33
	seed *= wyHashP3
	seed ^= seed >> 29
	seed *= wyHashP4
	seed ^= seed >> 32

	return seed
}

// hashUdpEndpointKey computes a 64-bit hash of a UdpEndpointKey.
// It mainly hashes the Src address to maintain consistency with historical
// shard selection, but incorporates Dst and RouteScope for better distribution
// across destination-affine and metadata-sensitive UDP flows.
func hashUdpEndpointKey(key UdpEndpointKey) uint64 {
	h := hashAddrPort(key.Src)
	if key.Dst.Port() != 0 {
		h ^= hashAddrPort(key.Dst)
	}
	if key.RouteScope.Outbound != 0 ||
		key.RouteScope.Mark != 0 ||
		key.RouteScope.Dscp != 0 ||
		key.RouteScope.Pname != [16]uint8{} ||
		key.RouteScope.Mac != [6]uint8{} {
		scopeSeed := uint64(key.RouteScope.Outbound)<<56 |
			uint64(key.RouteScope.Dscp)<<48 |
			uint64(key.RouteScope.Mark)
		h ^= wyMix(scopeSeed^wyHashP0, wyHashP1)
		scopePnameHi := wyRead64(key.RouteScope.Pname[:8])
		scopePnameLo := wyRead64(key.RouteScope.Pname[8:])
		h ^= wyMix(scopePnameHi^wyHashP2, scopePnameLo^wyHashP3)
		var macBuf [8]byte
		copy(macBuf[:], key.RouteScope.Mac[:])
		h ^= wyMix(wyRead64(macBuf[:])^wyHashP4, wyHashP0)
	}
	return h
}
