package testhelpers

import (
	"testing"
	"time"

	"github.com/gofrs/uuid"
	"github.com/golang/protobuf/proto"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	eventbus "code.justin.tv/eventbus/client"
	"code.justin.tv/eventbus/client/internal/testschema/user_password"
)

func TestSNSPayload(t *testing.T) {
	// use a fixed date & UUID so that the outputs are idempotent.
	when := time.Date(2020, 1, 4, 14, 25, 0, 0, time.UTC)
	msgID := uuid.Must(uuid.FromString("ED280816-E404-444A-A2D9-FFD2D171F928"))

	message := &user_password.Update{
		UserId: "1234",
	}

	raw, err := BinaryPayload(message, when, msgID)
	assert.NoError(t, err)
	assert.Equal(t, "AQAuChDtKAgW5ARESqLZ_9LRcfkoEhJVc2VyUGFzc3dvcmRVcGRhdGUaBgi8wMLwBQoEMTIzNA", raw.SNSPayload())
	assert.NotNil(t, raw.FakeSNSMessage())
}

func TestFakeSNSMessage(t *testing.T) {
	// use a fixed date & UUID so that the outputs are idempotent.
	when := time.Date(2020, 1, 4, 14, 26, 0, 0, time.UTC)
	msgID := uuid.Must(uuid.FromString("ED280816-E404-444A-A2D9-FFD2D171F928"))

	message := &user_password.Update{
		UserId: "1235",
	}

	raw, err := BinaryPayload(message, when, msgID)
	assert.NoError(t, err)

	snsMessage := raw.FakeSNSMessage()
	assert.NotEmpty(t, snsMessage.MessageID)
	assert.Equal(t, "AQAuChDtKAgW5ARESqLZ_9LRcfkoEhJVc2VyUGFzc3dvcmRVcGRhdGUaBgj4wMLwBQoEMTIzNQ", snsMessage.Message)
	assert.NotZero(t, snsMessage.Timestamp)
	assert.NotEmpty(t, snsMessage.TopicArn)

	// We are overriding the message ID and timestamp so that we get idempotent outputs
	snsMessage.MessageID = "8E7C2F0A-6BB8-485C-917E-6B605A0DDF29"
	snsMessage.Timestamp = when.Add(1 * time.Second)

	sqsBody, err := snsMessage.SQSBody()
	assert.NoError(t, err)
	assert.Equal(t,
		`{"MessageId":"8E7C2F0A-6BB8-485C-917E-6B605A0DDF29","Type":"Notification","TopicArn":"arn:aws:sns:us-west-2:123456789012:eventbus-fake","Timestamp":"2020-01-04T14:26:01Z","Message":"AQAuChDtKAgW5ARESqLZ_9LRcfkoEhJVc2VyUGFzc3dvcmRVcGRhdGUaBgj4wMLwBQoEMTIzNQ"}`,
		sqsBody,
	)
}

func TestMakePayload(t *testing.T) {
	messageID := "ed280816-e404-444a-a2d9-ffd2d171f928"
	header := &eventbus.Header{
		CreatedAt:   time.Date(2020, 1, 4, 14, 25, 0, 0, time.UTC),
		MessageID:   uuid.Must(uuid.FromString(messageID)),
		Environment: "production",
	}

	message := &user_password.Update{
		UserId: "1234",
	}

	raw, err := MakePayload(header, message)
	require.NoError(t, err)
	assert.Equal(t, "AQA6ChDtKAgW5ARESqLZ_9LRcfkoEhJVc2VyUGFzc3dvcmRVcGRhdGUaBgi8wMLwBSoKcHJvZHVjdGlvbgoEMTIzNA", raw.SNSPayload())

	t.Run("Decodes", func(t *testing.T) {
		msg, err := DecodeBinaryPayload(raw.Bytes())
		require.NoError(t, err)
		assert.Equal(t, header.CreatedAt, msg.Header.CreatedAt)
		assert.Equal(t, "UserPasswordUpdate", msg.Header.EventType)
		assert.Equal(t, messageID, msg.Header.MessageID.String())
	})
}

func TestMakePayload_defaults(t *testing.T) {
	header := &eventbus.Header{
		Environment: "production",
	}

	message := &user_password.Update{
		UserId: "1234",
	}

	raw, err := MakePayload(header, message)
	require.NoError(t, err)
	assert.NotEmpty(t, raw.Bytes())

	// see that we modified the header and set values.
	assert.Equal(t, "UserPasswordUpdate", header.EventType)
	assert.True(t, header.CreatedAt.After(time.Now().Add(-5*time.Second)))
	assert.NotEqual(t, header.MessageID, uuid.Nil)
}

func TestDecodeSNSPayload(t *testing.T) {
	msg, err := DecodeSNSPayload("AQAuChDtKAgW5ARESqLZ_9LRcfkoEhJVc2VyUGFzc3dvcmRVcGRhdGUaBgj4wMLwBQoEMTIzNQ")
	assert.NoError(t, err)
	require.NotNil(t, msg)
	assert.Equal(t, "UserPasswordUpdate", msg.Header.EventType)
	assert.Len(t, msg.Payload, 6)

	var dest user_password.Update
	err = proto.Unmarshal(msg.Payload, &dest)
	assert.NoError(t, err)
	assert.Equal(t, "1235", dest.GetUserId())
}
