package dialer

import (
	"encoding/binary"
	"net"
	"unsafe"

	"golang.org/x/sys/windows"
)

const (
	IP_UNICAST_IF   = 31
	IPV6_UNICAST_IF = 31
)

func bindDevice(network, address string, fd uintptr, ifceName string) error {
	if ifceName == "" {
		return nil
	}

	host, _, _ := net.SplitHostPort(address)
	ip := net.ParseIP(host)
	if ip != nil && !ip.IsGlobalUnicast() {
		return nil
	}

	ifce, err := net.InterfaceByName(ifceName)
	if err != nil {
		return err
	}

	switch network {
	case "tcp", "tcp4", "udp", "udp4":
		return bindSocketToInterface4(windows.Handle(fd), uint32(ifce.Index))
	case "tcp6", "udp6":
		err = bindSocketToInterface6(windows.Handle(fd), uint32(ifce.Index))
		if network == "udp6" && ip == nil {
			// The underlying IP net maybe IPv4 even if the `network` param is `udp6`,
			// so we should bind socket to interface4 at the same time.
			err = bindSocketToInterface4(windows.Handle(fd), uint32(ifce.Index))
		}
		return err
	}

	return nil
}

func setMark(fd uintptr, mark int) error {
	return nil
}

// switchNetns is a no-op stub — network namespace switching is a Linux-only feature.
func switchNetns(name string) (restore func(), err error) {
	return nil, nil
}

func bindSocketToInterface4(handle windows.Handle, index uint32) error {
	// For IPv4, this parameter must be an interface index in network byte order.
	// Ref: https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options
	var bytes [4]byte
	binary.BigEndian.PutUint32(bytes[:], index)
	index = *(*uint32)(unsafe.Pointer(&bytes[0]))
	return windows.SetsockoptInt(handle, windows.IPPROTO_IP, IP_UNICAST_IF, int(index))
}

func bindSocketToInterface6(handle windows.Handle, index uint32) error {
	return windows.SetsockoptInt(handle, windows.IPPROTO_IPV6, IPV6_UNICAST_IF, int(index))
}
