package peer

import (
	"bytes"
	"net"
	"net/netip"
	"sync"
)

type TestPacket struct {
	Addr netip.AddrPort
	Data []byte
}

type TestNetwork struct {
	lock    sync.Mutex
	packets map[netip.AddrPort]chan TestPacket
}

func NewTestNetwork() *TestNetwork {
	return &TestNetwork{packets: map[netip.AddrPort]chan TestPacket{}}
}

func (n *TestNetwork) NewUDPConn(localAddr netip.AddrPort) *TestUDPConn {
	n.lock.Lock()
	defer n.lock.Unlock()
	if _, ok := n.packets[localAddr]; !ok {
		n.packets[localAddr] = make(chan TestPacket, 1024)
	}
	return &TestUDPConn{
		addr:    localAddr,
		n:       n,
		packets: n.packets[localAddr],
	}
}

func (n *TestNetwork) write(b []byte, from, to netip.AddrPort) {
	n.lock.Lock()
	defer n.lock.Unlock()
	if _, ok := n.packets[to]; !ok {
		n.packets[to] = make(chan TestPacket, 1024)
	}
	n.packets[to] <- TestPacket{
		Addr: from,
		Data: bytes.Clone(b),
	}
}

type TestUDPConn struct {
	addr    netip.AddrPort
	n       *TestNetwork
	packets chan TestPacket
}

func (c *TestUDPConn) WriteToUDPAddrPort(b []byte, addr netip.AddrPort) (int, error) {
	c.n.write(b, c.addr, addr)
	return len(b), nil
}

func (c *TestUDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
	return c.WriteToUDPAddrPort(b, addr.AddrPort())
}

func (c *TestUDPConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
	pkt := <-c.packets
	b = b[:len(pkt.Data)]
	copy(b, pkt.Data)
	return len(b), pkt.Addr, nil
}

func (c *TestUDPConn) Packets() (out []TestPacket) {
	for {
		select {
		case pkt := <-c.packets:
			out = append(out, pkt)
		default:
			return
		}
	}
}
