package wsconnection

import (
	"sync"
	"testing"
	"time"

	"github.com/gofrs/uuid"
	"github.com/stretchr/testify/assert"

	"code.justin.tv/websocket-edge/server/internal/metrics"
	"code.justin.tv/websocket-edge/server/mocks"
	"code.justin.tv/websocket-edge/server/protocol"
)

func TestSendMessages(t *testing.T) {
	t.Run("Forward", func(t *testing.T) {
		t.Run("adds messages to send buffer", func(t *testing.T) {
			mockConn := &mocks.Conn{}
			sessionID := uuid.Must(uuid.NewV4()).String()
			sendBuffer := make(chan []byte, 1)
			wsConn := connection{
				Conn:       mockConn,
				sendBuffer: sendBuffer,
				ID:         sessionID,
			}

			testValue := "message goes here"
			testMessage := protocol.ServiceToClientMessage{
				sessionID,
				testValue,
			}
			err := wsConn.Forward(testMessage)
			assert.NoError(t, err)

			valOnSendBuffer := <-sendBuffer
			assert.Equal(t, testValue, string(valOnSendBuffer))
		})
	})

	t.Run("cleanup", func(t *testing.T) {
		t.Run("calls cleanup hook, closes buffer, stops db updates", func(t *testing.T) {
			mockConn := &mocks.Conn{}
			sessionID := uuid.Must(uuid.NewV4()).String()
			sendBuffer := make(chan []byte, 1)

			cleanupHookCalled := false
			shutdown := make(chan interface{}, 1)

			wsConn := connection{
				Conn:        mockConn,
				sendBuffer:  sendBuffer,
				ID:          sessionID,
				cleanupOnce: &sync.Once{},
				cleanupHook: func() { cleanupHookCalled = true },
				shutdown:    shutdown,
				statter:     &metrics.Noop{},
			}
			mockConn.On("Close").Return(nil)

			wsConn.cleanup()

			// Verify message sent to stop db updates
			select {
			case <-shutdown:
			case <-time.After(1 * time.Second):
				t.Fail()
			}
			// Verify cleanup hook was called
			assert.Equal(t, true, cleanupHookCalled)

			// We test that the send buffer was closed by sending to it, which will panic.
			defer func() {
				if r := recover(); r == nil {
					t.Fail()
				}
			}()
			wsConn.sendBuffer <- []byte{}
		})
	})
}
