package transport

import (
	"io"
	"sync"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestBytesRead(t *testing.T) {
	t.Run("test concurrency", func(t *testing.T) {
		// Test for the race detector, to verify a Read that doesn't yield any bytes
		// is okay to use from multiple goroutines. This was our historic behavior.
		// See golang.org/issue/7856
		r := bytesReader{s: &([]byte{})}
		var wg sync.WaitGroup
		for i := 0; i < 5; i++ {
			wg.Add(2)
			go func() {
				defer wg.Done()
				var buf [1]byte
				r.Read(buf[:])
			}()
			go func() {
				defer wg.Done()
				r.Read(nil)
			}()
		}
		wg.Wait()
	})

	t.Run("fail to read if pointer is nil", func(t *testing.T) {
		n, err := (&bytesReader{}).Read(nil)
		require.Equal(t, 0, n)
		require.EqualError(t, err, "byte slice pointer is nil")
	})

	t.Run("read using buffer", func(t *testing.T) {
		data := []byte("0123456789")
		r := bytesReader{s: &data}

		got := make([]byte, 0, 11)
		buf := make([]byte, 1)
		for {
			n, err := r.Read(buf)
			if n < 0 {
				require.Fail(t, "unexpected bytes read size")
			}
			got = append(got, buf[:n]...)
			if err != nil {
				if err == io.EOF {
					break
				}
				require.Fail(t, "unexpected error while reading", err.Error())
			}
		}
		require.Equal(t, "0123456789", string(got))
	})

	t.Run("read updated pointer value", func(t *testing.T) {
		data := []byte("0123456789")
		pointer := &data
		r := bytesReader{s: pointer}
		data[2] = []byte("9")[0]

		got := make([]byte, 0, 11)
		buf := make([]byte, 1)
		for {
			n, err := r.Read(buf)
			if n < 0 {
				require.Fail(t, "unexpected bytes read size")
			}
			got = append(got, buf[:n]...)
			if err != nil {
				if err == io.EOF {
					break
				}
				require.Fail(t, "unexpected error while reading", err.Error())
			}
		}
		require.Equal(t, "0193456789", string(got))
	})

	t.Run("read using buffer multiple times", func(t *testing.T) {
		data := []byte("0123456789")
		r := bytesReader{s: &data}

		got := make([]byte, 0, 11)
		buf := make([]byte, 1)
		for {
			n, err := r.Read(buf)
			if n < 0 {
				require.Fail(t, "unexpected bytes read size")
			}
			got = append(got, buf[:n]...)
			if err != nil {
				if err == io.EOF {
					break
				}
				require.Fail(t, "unexpected error while reading", err.Error())
			}
		}
		require.Equal(t, "0123456789", string(got))

		pos, err := r.Seek(0, io.SeekStart)
		require.NoError(t, err)
		require.Equal(t, int64(0), pos)

		got = make([]byte, 0, 11)
		for {
			n, err := r.Read(buf)
			if n < 0 {
				require.Fail(t, "unexpected bytes read size")
			}
			got = append(got, buf[:n]...)
			if err != nil {
				if err == io.EOF {
					break
				}
				require.Fail(t, "unexpected error while reading", err.Error())
			}
		}
		require.Equal(t, "0123456789", string(got))
	})
}
