package main

import (
	"context"
	"testing"

	"code.justin.tv/beefcake/server/internal/config"
	"code.justin.tv/beefcake/server/internal/legacyperm"
	"code.justin.tv/beefcake/server/internal/role/rolemocks"
	"code.justin.tv/beefcake/server/internal/testconfig"
	"code.justin.tv/beefcake/server/rpc/beefcake"
	"github.com/aws/aws-lambda-go/events"
	"github.com/stretchr/testify/mock"
)

type handlerTest struct {
	Handler *handler
	Config  *config.Config
	Roles   *rolemocks.RolesAPI
}

func newHandlerTest(t *testing.T) *handlerTest {
	config := testconfig.New(t)
	roles := new(rolemocks.RolesAPI)
	return &handlerTest{
		Handler: &handler{
			Config: config,
			Roles:  roles,
		},
		Config: config,
		Roles:  roles,
	}
}

func (ht *handlerTest) Teardown(t *testing.T) {
	ht.Roles.AssertExpectations(t)
}

func TestHandler(t *testing.T) {
	const testPermID = "testPermissionID"
	const testPermName = "testPermissionName"

	testCases := []struct {
		CaseName string

		OldPermID string
		OldRoles  []string
		NewPermID string
		NewRoles  []string

		AddedRoles   []string
		RemovedRoles []string
	}{
		{
			CaseName:     "perm created",
			OldPermID:    "",
			OldRoles:     []string{},
			NewPermID:    testPermID,
			NewRoles:     []string{},
			AddedRoles:   []string{},
			RemovedRoles: []string{},
		},
		{
			CaseName:  "first role added",
			OldPermID: testPermID,
			OldRoles:  []string{},
			NewPermID: testPermID,
			NewRoles: []string{
				"role1",
			},
			AddedRoles: []string{
				"role1",
			},
			RemovedRoles: []string{},
		},
		{
			CaseName:  "first role removed",
			OldPermID: testPermID,
			OldRoles: []string{
				"role1",
			},
			NewPermID:  testPermID,
			NewRoles:   []string{},
			AddedRoles: []string{},
			RemovedRoles: []string{
				"role1",
			},
		},
		{
			CaseName:  "role added",
			OldPermID: testPermID,
			OldRoles: []string{
				"role1",
			},
			NewPermID: testPermID,
			NewRoles: []string{
				"role1",
				"role2",
			},
			AddedRoles: []string{
				"role1",
				"role2",
			},
			RemovedRoles: []string{},
		},
		{
			CaseName:  "role removed",
			OldPermID: testPermID,
			OldRoles: []string{
				"role1",
				"role2",
			},
			NewPermID: testPermID,
			NewRoles: []string{
				"role1",
			},
			AddedRoles: []string{
				"role1",
			},
			RemovedRoles: []string{
				"role2",
			},
		},
		{
			CaseName:  "perm deleted with roles",
			OldPermID: testPermID,
			OldRoles: []string{
				"role1",
			},
			NewPermID:  "",
			NewRoles:   []string{},
			AddedRoles: []string{},
			RemovedRoles: []string{
				"role1",
			},
		},
		{
			CaseName:     "perm deleted",
			OldPermID:    testPermID,
			OldRoles:     []string{},
			NewPermID:    "",
			NewRoles:     []string{},
			AddedRoles:   []string{},
			RemovedRoles: []string{},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.CaseName, func(t *testing.T) {
			ht := newHandlerTest(t)
			defer ht.Teardown(t)

			for _, m := range tc.AddedRoles {
				ht.Roles.
					On("AddLegacyPermissionDetails", mock.Anything, m, &beefcake.Permission_LegacyPermission{
						Id:   testPermID,
						Name: testPermName,
					}).
					Return(nil).
					Once()
			}

			for _, m := range tc.RemovedRoles {
				ht.Roles.
					On("RemoveLegacyPermissionDetails", mock.Anything, m, testPermID).
					Return(nil).
					Once()
			}

			stringSliceToMap := func(in []string) events.DynamoDBAttributeValue {
				out := map[string]events.DynamoDBAttributeValue{}
				for _, val := range in {
					out[val] = events.NewBinaryAttribute(nil)
				}
				return events.NewMapAttribute(out)
			}

			ht.Handler.Handle(context.Background(), events.DynamoDBEvent{
				Records: []events.DynamoDBEventRecord{
					{
						Change: events.DynamoDBStreamRecord{
							OldImage: map[string]events.DynamoDBAttributeValue{
								ht.Config.LegacyPermissionsHashKey.Get(): events.NewStringAttribute(tc.OldPermID),
								legacyperm.NameAttribute:                 events.NewStringAttribute(testPermName),
								legacyperm.RolesAttribute:                stringSliceToMap(tc.OldRoles),
							},
							NewImage: map[string]events.DynamoDBAttributeValue{
								ht.Config.LegacyPermissionsHashKey.Get(): events.NewStringAttribute(tc.NewPermID),
								legacyperm.NameAttribute:                 events.NewStringAttribute(testPermName),
								legacyperm.RolesAttribute:                stringSliceToMap(tc.NewRoles),
							},
						},
					},
				},
			})
		})
	}
}
