package peer_protocol

import (
	"bufio"
	"bytes"
	"io"
	"sync"
	"testing"

	qt "github.com/frankban/quicktest"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func BenchmarkDecodePieces(t *testing.B) {
	const pieceLen = 1 << 14
	inputMsg := Message{
		Type:  Piece,
		Index: 0,
		Begin: 1,
		Piece: make([]byte, pieceLen),
	}
	b := inputMsg.MustMarshalBinary()
	t.SetBytes(int64(len(b)))
	var r bytes.Reader
	// Try to somewhat emulate what torrent.Client would do. But the goal is to get decoding as fast
	// as possible and let consumers apply their own adjustments.
	d := Decoder{
		R:         bufio.NewReaderSize(&r, 1<<10),
		MaxLength: 1 << 18,
		Pool: &sync.Pool{
			New: func() interface{} {
				b := make([]byte, pieceLen)
				return &b
			},
		},
	}
	c := qt.New(t)
	t.ReportAllocs()
	t.ResetTimer()
	for i := 0; i < t.N; i += 1 {
		r.Reset(b)
		var msg Message
		err := d.Decode(&msg)
		if err != nil {
			t.Fatal(err)
		}
		// This is very expensive, and should be discovered in tests rather than a benchmark.
		if false {
			c.Assert(msg, qt.DeepEquals, inputMsg)
		}
		// WWJD
		d.Pool.Put(&msg.Piece)
	}
}

func TestDecodeShortPieceEOF(t *testing.T) {
	r, w := io.Pipe()
	go func() {
		w.Write(Message{Type: Piece, Piece: make([]byte, 1)}.MustMarshalBinary())
		w.Close()
	}()
	d := Decoder{
		R:         bufio.NewReader(r),
		MaxLength: 1 << 15,
		Pool: &sync.Pool{New: func() interface{} {
			b := make([]byte, 2)
			return &b
		}},
	}
	var m Message
	require.NoError(t, d.Decode(&m))
	assert.Len(t, m.Piece, 1)
	assert.ErrorIs(t, d.Decode(&m), io.EOF)
}

func TestDecodeOverlongPiece(t *testing.T) {
	r, w := io.Pipe()
	go func() {
		w.Write(Message{Type: Piece, Piece: make([]byte, 3)}.MustMarshalBinary())
		w.Close()
	}()
	d := Decoder{
		R:         bufio.NewReader(r),
		MaxLength: 1 << 15,
		Pool: &sync.Pool{New: func() interface{} {
			b := make([]byte, 2)
			return &b
		}},
	}
	var m Message
	require.Error(t, d.Decode(&m))
}
