package buffer

import (
	"bufio"
	"bytes"
	"io"
	"testing"
	"time"

	"git.quad4.io/Networks/Reticulum-Go/pkg/channel"
	"git.quad4.io/Networks/Reticulum-Go/pkg/common"
	"git.quad4.io/Networks/Reticulum-Go/pkg/packet"
	"git.quad4.io/Networks/Reticulum-Go/pkg/transport"
)

func TestStreamDataMessage_Pack(t *testing.T) {
	tests := []struct {
		name       string
		streamID   uint16
		data       []byte
		eof        bool
		compressed bool
	}{
		{
			name:       "NormalMessage",
			streamID:   123,
			data:       []byte("test data"),
			eof:        false,
			compressed: false,
		},
		{
			name:       "EOFMessage",
			streamID:   456,
			data:       []byte("final data"),
			eof:        true,
			compressed: false,
		},
		{
			name:       "CompressedMessage",
			streamID:   789,
			data:       []byte("compressed data"),
			eof:        false,
			compressed: true,
		},
		{
			name:       "EmptyData",
			streamID:   0,
			data:       []byte{},
			eof:        false,
			compressed: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			msg := &StreamDataMessage{
				StreamID:   tt.streamID,
				Data:       tt.data,
				EOF:        tt.eof,
				Compressed: tt.compressed,
			}

			packed, err := msg.Pack()
			if err != nil {
				t.Fatalf("Pack() failed: %v", err)
			}

			if len(packed) < 2 {
				t.Error("Packed message too short")
			}

			unpacked := &StreamDataMessage{}
			if err := unpacked.Unpack(packed); err != nil {
				t.Fatalf("Unpack() failed: %v", err)
			}

			if unpacked.StreamID != tt.streamID {
				t.Errorf("StreamID = %d, want %d", unpacked.StreamID, tt.streamID)
			}
			if unpacked.EOF != tt.eof {
				t.Errorf("EOF = %v, want %v", unpacked.EOF, tt.eof)
			}
			if unpacked.Compressed != tt.compressed {
				t.Errorf("Compressed = %v, want %v", unpacked.Compressed, tt.compressed)
			}
			if !bytes.Equal(unpacked.Data, tt.data) {
				t.Errorf("Data = %v, want %v", unpacked.Data, tt.data)
			}
		})
	}
}

func TestStreamDataMessage_Unpack(t *testing.T) {
	tests := []struct {
		name      string
		data      []byte
		wantError bool
	}{
		{
			name:      "ValidMessage",
			data:      []byte{0x00, 0x7B, 'h', 'e', 'l', 'l', 'o'},
			wantError: false,
		},
		{
			name:      "TooShort",
			data:      []byte{0x00},
			wantError: true,
		},
		{
			name:      "Empty",
			data:      []byte{},
			wantError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			msg := &StreamDataMessage{}
			err := msg.Unpack(tt.data)
			if (err != nil) != tt.wantError {
				t.Errorf("Unpack() error = %v, wantError %v", err, tt.wantError)
			}
		})
	}
}

func TestStreamDataMessage_GetType(t *testing.T) {
	msg := &StreamDataMessage{}
	if msg.GetType() != 0x01 {
		t.Errorf("GetType() = %d, want 0x01", msg.GetType())
	}
}

func TestRawChannelReader_AddCallback(t *testing.T) {
	reader := &RawChannelReader{
		streamID:  1,
		buffer:    bytes.NewBuffer(nil),
		callbacks: make(map[int]func(int)),
	}

	cb := func(int) {}

	reader.AddReadyCallback(cb)
	if len(reader.callbacks) != 1 {
		t.Error("Callback should be added")
	}
}

func TestBuffer_Write(t *testing.T) {
	buf := &Buffer{
		ReadWriter: bufio.NewReadWriter(bufio.NewReader(bytes.NewBuffer(nil)), bufio.NewWriter(bytes.NewBuffer(nil))),
	}

	data := []byte("test")
	n, err := buf.Write(data)
	if err != nil {
		t.Errorf("Write() error = %v", err)
	}
	if n != len(data) {
		t.Errorf("Write() = %d bytes, want %d", n, len(data))
	}
}

func TestBuffer_Read(t *testing.T) {
	buf := &Buffer{
		ReadWriter: bufio.NewReadWriter(bufio.NewReader(bytes.NewBuffer([]byte("test data"))), bufio.NewWriter(bytes.NewBuffer(nil))),
	}

	data := make([]byte, 10)
	n, err := buf.Read(data)
	if err != nil && err != io.EOF {
		t.Errorf("Read() error = %v", err)
	}
	if n <= 0 {
		t.Errorf("Read() = %d bytes, want > 0", n)
	}
}

func TestBuffer_Close(t *testing.T) {
	buf := &Buffer{
		ReadWriter: bufio.NewReadWriter(bufio.NewReader(bytes.NewBuffer(nil)), bufio.NewWriter(bytes.NewBuffer(nil))),
	}

	if err := buf.Close(); err != nil {
		t.Errorf("Close() error = %v", err)
	}
}

func TestStreamIDMax(t *testing.T) {
	if StreamIDMax != 0x3fff {
		t.Errorf("StreamIDMax = %d, want %d", StreamIDMax, 0x3fff)
	}
}

func TestMaxChunkLen(t *testing.T) {
	if MaxChunkLen != 16*1024 {
		t.Errorf("MaxChunkLen = %d, want %d", MaxChunkLen, 16*1024)
	}
}

func TestMaxDataLen(t *testing.T) {
	if MaxDataLen != 457 {
		t.Errorf("MaxDataLen = %d, want %d", MaxDataLen, 457)
	}
}

type mockLink struct {
	status byte
	rtt    float64
}

func (m *mockLink) GetStatus() byte                                                       { return m.status }
func (m *mockLink) GetRTT() float64                                                       { return m.rtt }
func (m *mockLink) RTT() float64                                                          { return m.rtt }
func (m *mockLink) GetLinkID() []byte                                                     { return []byte("testlink") }
func (m *mockLink) Send(data []byte) interface{}                                          { return &packet.Packet{Raw: data} }
func (m *mockLink) Resend(p interface{}) error                                            { return nil }
func (m *mockLink) SetPacketTimeout(p interface{}, cb func(interface{}), t time.Duration) {}
func (m *mockLink) SetPacketDelivered(p interface{}, cb func(interface{}))                {}
func (m *mockLink) HandleInbound(pkt *packet.Packet) error                                { return nil }
func (m *mockLink) ValidateLinkProof(pkt *packet.Packet, networkIface common.NetworkInterface) error {
	return nil
}

func TestNewRawChannelReader(t *testing.T) {
	link := &mockLink{status: transport.STATUS_ACTIVE}
	ch := channel.NewChannel(link)
	reader := NewRawChannelReader(123, ch)

	if reader.streamID != 123 {
		t.Errorf("streamID = %d, want %d", reader.streamID, 123)
	}
	if reader.channel != ch {
		t.Error("channel not set correctly")
	}
	if reader.buffer == nil {
		t.Error("buffer is nil")
	}
	if reader.callbacks == nil {
		t.Error("callbacks is nil")
	}
}

func TestRawChannelReader_RemoveReadyCallback(t *testing.T) {
	reader := &RawChannelReader{
		streamID:  1,
		buffer:    bytes.NewBuffer(nil),
		callbacks: make(map[int]func(int)),
	}

	cb1 := func(int) {}
	cb2 := func(int) {}

	id1 := reader.AddReadyCallback(cb1)
	reader.AddReadyCallback(cb2)

	if len(reader.callbacks) != 2 {
		t.Errorf("callbacks length = %d, want 2", len(reader.callbacks))
	}

	reader.RemoveReadyCallback(id1)

	if len(reader.callbacks) != 1 {
		t.Errorf("RemoveReadyCallback did not remove callback, length = %d", len(reader.callbacks))
	}
}

func TestRawChannelReader_Read(t *testing.T) {
	reader := &RawChannelReader{
		streamID: 1,
		buffer:   bytes.NewBuffer([]byte("test data")),
		eof:      false,
	}

	data := make([]byte, 10)
	n, err := reader.Read(data)
	if err != nil {
		t.Errorf("Read() error = %v", err)
	}
	if n == 0 {
		t.Error("Read() returned 0 bytes")
	}

	reader.eof = true
	reader.buffer = bytes.NewBuffer(nil)
	n, err = reader.Read(data)
	if err != io.EOF {
		t.Errorf("Read() error = %v, want io.EOF", err)
	}
	if n != 0 {
		t.Errorf("Read() = %d bytes, want 0", n)
	}
}

func TestRawChannelReader_HandleMessage(t *testing.T) {
	reader := &RawChannelReader{
		streamID:  1,
		buffer:    bytes.NewBuffer(nil),
		callbacks: make(map[int]func(int)),
	}

	msg := &StreamDataMessage{
		StreamID:   1,
		Data:       []byte("test"),
		EOF:        false,
		Compressed: false,
	}

	called := false
	reader.AddReadyCallback(func(int) {
		called = true
	})

	result := reader.HandleMessage(msg)
	if !result {
		t.Error("HandleMessage() = false, want true")
	}
	if !called {
		t.Error("callback was not called")
	}
	if reader.buffer.Len() == 0 {
		t.Error("buffer is empty after HandleMessage")
	}

	msg.StreamID = 2
	result = reader.HandleMessage(msg)
	if result {
		t.Error("HandleMessage() = true, want false for different streamID")
	}

	msg.StreamID = 1
	msg.EOF = true
	reader.HandleMessage(msg)
	if !reader.eof {
		t.Error("EOF not set after HandleMessage with EOF flag")
	}
}

func TestNewRawChannelWriter(t *testing.T) {
	link := &mockLink{status: transport.STATUS_ACTIVE}
	ch := channel.NewChannel(link)
	writer := NewRawChannelWriter(456, ch)

	if writer.streamID != 456 {
		t.Errorf("streamID = %d, want %d", writer.streamID, 456)
	}
	if writer.channel != ch {
		t.Error("channel not set correctly")
	}
	if writer.eof {
		t.Error("eof should be false initially")
	}
}

func TestRawChannelWriter_Write(t *testing.T) {
	link := &mockLink{status: transport.STATUS_ACTIVE}
	ch := channel.NewChannel(link)
	writer := NewRawChannelWriter(1, ch)

	data := []byte("test data")
	n, err := writer.Write(data)
	if err != nil {
		t.Errorf("Write() error = %v", err)
	}
	if n != len(data) {
		t.Errorf("Write() = %d bytes, want %d", n, len(data))
	}

	largeData := make([]byte, MaxChunkLen+100)
	n, err = writer.Write(largeData)
	if err != nil {
		t.Errorf("Write() error = %v", err)
	}
	if n != MaxChunkLen {
		t.Errorf("Write() = %d bytes, want %d", n, MaxChunkLen)
	}
}

func TestRawChannelWriter_Close(t *testing.T) {
	link := &mockLink{status: transport.STATUS_ACTIVE}
	ch := channel.NewChannel(link)
	writer := NewRawChannelWriter(1, ch)

	if writer.eof {
		t.Error("EOF should be false before Close()")
	}

	err := writer.Close()
	if err != nil {
		t.Errorf("Close() error = %v", err)
	}
	if !writer.eof {
		t.Error("EOF should be true after Close()")
	}
}

func TestCreateReader(t *testing.T) {
	link := &mockLink{status: transport.STATUS_ACTIVE}
	ch := channel.NewChannel(link)
	callback := func(int) {}
	reader := CreateReader(789, ch, callback)

	if reader == nil {
		t.Error("CreateReader() returned nil")
	}
}

func TestCreateWriter(t *testing.T) {
	link := &mockLink{status: transport.STATUS_ACTIVE}
	ch := channel.NewChannel(link)
	writer := CreateWriter(101, ch)

	if writer == nil {
		t.Error("CreateWriter() returned nil")
	}
}

func TestCreateBidirectionalBuffer(t *testing.T) {
	link := &mockLink{status: transport.STATUS_ACTIVE}
	ch := channel.NewChannel(link)
	callback := func(int) {}
	buf := CreateBidirectionalBuffer(1, 2, ch, callback)

	if buf == nil {
		t.Error("CreateBidirectionalBuffer() returned nil")
	}
}

func TestCompressData(t *testing.T) {
	data := []byte("test data for compression")
	compressed := compressData(data)

	if compressed == nil {
		t.Skip("compressData() returned nil (compression implementation may be incomplete)")
	}
}

func TestDecompressData(t *testing.T) {
	data := []byte("test data")
	compressed := compressData(data)
	if compressed == nil {
		t.Skip("compression not working, skipping decompression test")
	}

	decompressed := decompressData(compressed)
	if decompressed == nil {
		t.Error("decompressData() returned nil")
	}
}
