package wire_test

import (
	"testing"
	"time"

	uuid "github.com/gofrs/uuid"
	"github.com/golang/protobuf/ptypes/timestamp"
	"github.com/stretchr/testify/require"

	"code.justin.tv/eventbus/client/internal/testevents"
	"code.justin.tv/eventbus/client/internal/wire"
)

func TestEncode(t *testing.T) {
	t.Run("Encoding low-level", func(t *testing.T) {
		t.Run("With fixed time and message type", func(t *testing.T) {
			header := &wire.HeaderV1{
				MessageId:   []byte("0123456789abcdef"),
				EventType:   "ClockTick",
				Environment: "staging",
				CreatedAt: &timestamp.Timestamp{
					Seconds: 12345,
					Nanos:   0,
				},
			}
			tick := testevents.NewClockUpdate(1234)
			buf, err := wire.EncodeRaw(header, tick)
			require.NoError(t, err)

			t.Run("Version and length bytes make sense", func(t *testing.T) {
				require.Len(t, buf, 51)
				require.Equal(t, byte(0x01), buf[0])
				require.Equal(t, []byte{0x00, 0x2b}, buf[1:3])
			})

			t.Run("Can Decode back", func(t *testing.T) {
				h, payloadBytes, err := wire.HeaderAndPayload(buf)
				require.NoError(t, err)
				require.Equal(t, "ClockTick", h.EventType)
				require.Equal(t, "staging", h.Environment)
				require.Len(t, payloadBytes, 5)
			})
		})

		t.Run("Standard Encode", func(t *testing.T) {
			now := time.Now().UTC()
			tick := testevents.NewClockUpdate(1234)
			buf, err := wire.DefaultsEncode(tick, "development")
			require.NoError(t, err)

			t.Run("Yields sensible header values", func(t *testing.T) {
				h, payloadBytes, err := wire.HeaderAndPayload(buf)
				require.NoError(t, err)
				require.Equal(t, "ClockUpdate", h.EventType)
				require.Equal(t, "development", h.Environment)
				require.InDelta(t, now.Unix(), h.CreatedAt.Seconds, 2) // Give 2 seconds leeway for the times to be within each other.
				u, err := uuid.FromBytes(h.MessageId)
				require.NoError(t, err)
				require.Equal(t, byte(4), u.Version())
				require.Len(t, payloadBytes, 5)
			})
		})
	})
}

func TestDecode(t *testing.T) {
	t.Run("Error Handling", func(t *testing.T) {
		t.Run("too-short message", func(t *testing.T) {
			_, _, err := wire.HeaderAndPayload([]byte{0x01})
			require.Error(t, err)
			require.Equal(t, "Message too short", err.Error())
		})

		t.Run("wrong version", func(t *testing.T) {
			_, _, err := wire.HeaderAndPayload([]byte{0x07, 0x05, 0x07})
			require.Error(t, err)
			require.Equal(t, "Unknown message version", err.Error())
		})

		t.Run("bad header", func(t *testing.T) {
			_, _, err := wire.HeaderAndPayload([]byte{0x01, 0x00, 0x01, 0xFC})
			require.Error(t, err)
			require.Equal(t, "Could not decode header: unexpected EOF", err.Error())
		})

		// TODO bad length test
	})
}
