package vpn

import (
	"encoding/binary"
	"io"
	"net/netip"

	"github.com/gofrs/uuid/v5"
	"github.com/sagernet/sing/common"
	"github.com/sagernet/sing/common/buf"
	E "github.com/sagernet/sing/common/exceptions"
	M "github.com/sagernet/sing/common/metadata"
)

const (
	Version = 0
)

const (
	CommandInbound = 1
	CommandTCP     = 2
)

type IPv4 [4]byte

var Destination = M.Socksaddr{
	Fqdn: "sp.vpn.sing-box.arpa",
	Port: 444,
}

var Loopback = netip.AddrFrom4([4]byte{127, 0, 0, 1})

var AddressSerializer = M.NewSerializer(
	M.AddressFamilyByte(0x01, M.AddressFamilyIPv4),
	M.AddressFamilyByte(0x03, M.AddressFamilyIPv6),
	M.AddressFamilyByte(0x02, M.AddressFamilyFqdn),
	M.PortThenAddress(),
)

type ClientRequest struct {
	Key         uuid.UUID
	Command     byte
	Gateway     IPv4
	Destination M.Socksaddr
}

func ReadClientRequest(reader io.Reader) (*ClientRequest, error) {
	var request ClientRequest
	var version uint8
	err := binary.Read(reader, binary.BigEndian, &version)
	if err != nil {
		return nil, err
	}
	if version != Version {
		return nil, E.New("unknown version: ", version)
	}
	_, err = io.ReadFull(reader, request.Key[:])
	if err != nil {
		return nil, err
	}
	err = binary.Read(reader, binary.BigEndian, &request.Command)
	if err != nil {
		return nil, err
	}
	_, err = io.ReadFull(reader, request.Gateway[:])
	if err != nil {
		return nil, err
	}
	request.Destination, err = AddressSerializer.ReadAddrPort(reader)
	if err != nil {
		return nil, err
	}
	return &request, nil
}

func WriteClientRequest(writer io.Writer, request *ClientRequest) error {
	var requestLen int
	requestLen += 1  // version
	requestLen += 16 // key
	requestLen += 1  // command
	requestLen += 4  // gateway
	requestLen += AddressSerializer.AddrPortLen(request.Destination)
	buffer := buf.NewSize(requestLen)
	defer buffer.Release()
	common.Must(
		buffer.WriteByte(Version),
		common.Error(buffer.Write(request.Key[:])),
		buffer.WriteByte(request.Command),
		common.Error(buffer.Write(request.Gateway[:])),
		AddressSerializer.WriteAddrPort(buffer, request.Destination),
	)
	return common.Error(writer.Write(buffer.Bytes()))
}

type ServerRequest struct {
	Source      IPv4
	Destination M.Socksaddr
}

func ReadServerRequest(reader io.Reader) (*ServerRequest, error) {
	var request ServerRequest
	_, err := io.ReadFull(reader, request.Source[:])
	if err != nil {
		return nil, err
	}
	request.Destination, err = AddressSerializer.ReadAddrPort(reader)
	if err != nil {
		return nil, err
	}
	return &request, nil
}

func WriteServerRequest(writer io.Writer, request *ServerRequest) error {
	var requestLen int
	requestLen += 4 // source
	requestLen += AddressSerializer.AddrPortLen(request.Destination)
	buffer := buf.NewSize(requestLen)
	defer buffer.Release()
	common.Must(
		common.Error(buffer.Write(request.Source[:])),
		AddressSerializer.WriteAddrPort(buffer, request.Destination),
	)
	return common.Error(writer.Write(buffer.Bytes()))
}
