package protocol

import (
	"bytes"
	"compress/zlib"
	"encoding/binary"
	"io"
	"math"
)

func ZlibCompress(data []byte) []byte {
	var b bytes.Buffer
	w := zlib.NewWriter(&b)
	w.Write(data)
	w.Close()
	return b.Bytes()
}

// Codec Functions (migrated from main.go)

func ReadVarint(r io.ByteReader) (uint64, error) {
	return binary.ReadUvarint(r)
}

func ReadVarintInt64(r io.ByteReader) (int64, error) {
	v, err := binary.ReadUvarint(r)
	return int64(v), err
}

func EncodeVarint(v uint64) []byte {
	buf := make([]byte, binary.MaxVarintLen64)
	n := binary.PutUvarint(buf, v)
	return buf[:n]
}

func EncodeDouble(v float64) []byte {
	buf := make([]byte, 8)
	binary.LittleEndian.PutUint64(buf, math.Float64bits(v))
	return buf
}

func SkipField(reader *bytes.Reader, wireType uint64) {
	switch wireType {
	case 0: // Varint
		ReadVarint(reader)
	case 1: // Fixed64
		reader.Seek(8, io.SeekCurrent)
	case 2: // Length-delimited
		length, _ := ReadVarint(reader)
		reader.Seek(int64(length), io.SeekCurrent)
	case 5: // Fixed32
		reader.Seek(4, io.SeekCurrent)
	}
}

// Helper to read a packet length (4 bytes, Big Endian) as per official protocol
func ReadPacketLength(conn io.Reader) (uint32, error) {
	var length uint32
	err := binary.Read(conn, binary.BigEndian, &length)
	return length, err
}

// WrapPacket encapsulates a payload in the Message and Packet structure
// 1. Message wrapper (Field 1: requestNumber, Field [requestNumber]: payload field)
// 2. Packet wrapper (Field 1: packetID, Field 3: Message bytes)
// 3. 4-byte Big Endian length prefix
func WrapPacket(requestNumber int, payload []byte, packetID uint32) []byte {
	// 1. Wrap in Message
	msg := make([]byte, 0)
	// Field 1: request_number (Varint)
	msg = append(msg, 0x08)
	msg = append(msg, EncodeVarint(uint64(requestNumber))...)

	// Field [requestNumber]: The actual response payload
	msg = append(msg, EncodeVarint(uint64(uint32(requestNumber)<<3|2))...)
	msg = append(msg, EncodeVarint(uint64(len(payload)))...)
	msg = append(msg, payload...)

	// 2. Wrap in Packet
	packet := make([]byte, 0)
	// Field 1: Id (Varint)
	packet = append(packet, 0x08)
	packet = append(packet, EncodeVarint(uint64(packetID))...)
	// Field 3: Payload (Message)
	packet = append(packet, 0x1a)
	packet = append(packet, EncodeVarint(uint64(len(msg)))...)
	packet = append(packet, msg...)

	// 3. Add 4-byte length prefix
	final := make([]byte, 4)
	binary.BigEndian.PutUint32(final, uint32(len(packet)))
	final = append(final, packet...)

	return final
}
