package v2raykcp

import (
	"crypto/rand"
	"encoding/binary"
)

// used only by KCP to add an obfuscating header before encrypted payload.
type PacketHeader interface {
	Size() int
	Serialize([]byte)
}

// NewPacketHeader creates a new PacketHeader instance for the given header type.
// Supported values: none, srtp, utp, wechat-video,
// dtls, wireguard. Unknown types fall back to no header.
func NewPacketHeader(headerType string) PacketHeader {
	switch headerType {
	case "srtp":
		return newSRTPHeader()
	case "utp":
		return newUTPHeader()
	case "wechat-video":
		return newWechatVideoHeader()
	case "dtls":
		return newDTLSHeader()
	case "wireguard":
		return newWireguardHeader()
	default:
		return nil
	}
}

// HeaderSize returns the byte size of the header for the given type.
func HeaderSize(headerType string) int {
	switch headerType {
	case "srtp", "utp", "wireguard":
		return 4
	case "wechat-video", "dtls":
		return 13
	default:
		return 0
	}
}

// ----- SRTP -----

type srtpHeader struct {
	header uint16
	number uint16
}

func newSRTPHeader() *srtpHeader {
	return &srtpHeader{
		header: 0xB5E8,
		number: randomUint16(),
	}
}

func (*srtpHeader) Size() int {
	return 4
}

func (s *srtpHeader) Serialize(b []byte) {
	s.number++
	binary.BigEndian.PutUint16(b, s.header)
	binary.BigEndian.PutUint16(b[2:], s.number)
}

// ----- UTP -----

type utpHeader struct {
	header       byte
	extension    byte
	connectionID uint16
}

func newUTPHeader() *utpHeader {
	return &utpHeader{
		header:       1,
		extension:    0,
		connectionID: randomUint16(),
	}
}

func (*utpHeader) Size() int {
	return 4
}

func (u *utpHeader) Serialize(b []byte) {
	binary.BigEndian.PutUint16(b, u.connectionID)
	b[2] = u.header
	b[3] = u.extension
}

// ----- WeChat Video -----

type wechatVideoHeader struct {
	sn uint32
}

func newWechatVideoHeader() *wechatVideoHeader {
	return &wechatVideoHeader{
		sn: randomUint32(),
	}
}

func (*wechatVideoHeader) Size() int {
	return 13
}

func (vc *wechatVideoHeader) Serialize(b []byte) {
	vc.sn++
	b[0] = 0xa1
	b[1] = 0x08
	binary.BigEndian.PutUint32(b[2:], vc.sn)
	b[6] = 0x00
	b[7] = 0x10
	b[8] = 0x11
	b[9] = 0x18
	b[10] = 0x30
	b[11] = 0x22
	b[12] = 0x30
}

// ----- DTLS -----

type dtlsHeader struct {
	epoch    uint16
	length   uint16
	sequence uint32
}

func newDTLSHeader() *dtlsHeader {
	return &dtlsHeader{
		epoch:    randomUint16(),
		sequence: 0,
		length:   17,
	}
}

func (*dtlsHeader) Size() int {
	return 13
}

func (d *dtlsHeader) Serialize(b []byte) {
	b[0] = 23 // application data
	b[1] = 254
	b[2] = 253
	b[3] = byte(d.epoch >> 8)
	b[4] = byte(d.epoch)
	b[5] = 0
	b[6] = 0
	b[7] = byte(d.sequence >> 24)
	b[8] = byte(d.sequence >> 16)
	b[9] = byte(d.sequence >> 8)
	b[10] = byte(d.sequence)
	d.sequence++
	b[11] = byte(d.length >> 8)
	b[12] = byte(d.length)
	d.length += 17
	if d.length > 100 {
		d.length -= 50
	}
}

// ----- WireGuard -----

type wireguardHeader struct{}

func newWireguardHeader() *wireguardHeader {
	return &wireguardHeader{}
}

func (*wireguardHeader) Size() int {
	return 4
}

func (*wireguardHeader) Serialize(b []byte) {
	b[0] = 0x04
	b[1] = 0x00
	b[2] = 0x00
	b[3] = 0x00
}

// ----- helpers -----

func randomUint16() uint16 {
	var b [2]byte
	if _, err := rand.Read(b[:]); err != nil {
		return 0
	}
	return binary.BigEndian.Uint16(b[:])
}

func randomUint32() uint32 {
	var b [4]byte
	if _, err := rand.Read(b[:]); err != nil {
		return 0
	}
	return binary.BigEndian.Uint32(b[:])
}
