package accesscontrol

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/grafana/grafana/pkg/apimachinery/identity"
	ac "github.com/grafana/grafana/pkg/services/accesscontrol"
	"github.com/grafana/grafana/pkg/services/ngalert/models"
	"github.com/grafana/grafana/pkg/services/org"
)

func TestReceiverAccess(t *testing.T) {
	recv1 := models.ReceiverGen(models.ReceiverMuts.WithName("test receiver 1"), models.ReceiverMuts.WithValidIntegration("slack"))()
	recv2 := models.ReceiverGen(models.ReceiverMuts.WithName("test receiver 2"), models.ReceiverMuts.WithValidIntegration("email"))()
	recv3 := models.ReceiverGen(models.ReceiverMuts.WithName("test receiver 3"), models.ReceiverMuts.WithValidIntegration("webhook"))()

	allReceivers := []*models.Receiver{
		&recv1,
		&recv2,
		&recv3,
	}

	permissions := func(perms ...models.ReceiverPermission) models.ReceiverPermissionSet {
		set := models.NewReceiverPermissionSet()
		for _, v := range models.ReceiverPermissions() {
			set.Set(v, false)
		}
		for _, v := range perms {
			set.Set(v, true)
		}
		return set
	}

	testCases := []struct {
		name                     string
		user                     identity.Requester
		expected                 map[string]models.ReceiverPermissionSet
		expectedWithProvisioning map[string]models.ReceiverPermissionSet
	}{
		// Legacy read.
		{
			name: "legacy global reader should have no elevated permissions",
			user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingNotificationsRead}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		{
			name: "legacy global notifications provisioning reader should have no elevated permissions",
			user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingNotificationsProvisioningRead}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		{
			name: "legacy global provisioning reader should have no elevated permissions",
			user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingProvisioningRead}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		{
			name: "legacy global provisioning secret reader should have secret permissions on provisioning only",
			user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingProvisioningReadSecrets}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
			expectedWithProvisioning: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionReadSecret),
				recv2.UID: permissions(models.ReceiverPermissionReadSecret),
				recv3.UID: permissions(models.ReceiverPermissionReadSecret),
			},
		},
		// Receiver read.
		{
			name: "global receiver reader should have no elevated permissions",
			user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversRead, Scope: models.ScopeReceiversAll}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		{
			name: "global receiver secret reader should have secret permissions",
			user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversAll}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionReadSecret),
				recv2.UID: permissions(models.ReceiverPermissionReadSecret),
				recv3.UID: permissions(models.ReceiverPermissionReadSecret),
			},
		},
		{
			name: "per-receiver secret reader should have per-receiver",
			user: newEmptyUser(
				ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionReadSecret),
				recv2.UID: permissions(),
				recv3.UID: permissions(models.ReceiverPermissionReadSecret),
			},
		},
		// Legacy write.
		{
			name: "legacy global writer should have full write",
			user: newViewUser(ac.Permission{Action: ac.ActionAlertingNotificationsWrite}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
				recv2.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
				recv3.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
			},
		},
		{
			name: "legacy writers should require read",
			user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingNotificationsWrite}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		//{
		//	name: "legacy global notifications provisioning writer should have full write on provisioning only",
		//	user: newViewUser(ac.Permission{Action: ac.ActionAlertingNotificationsProvisioningWrite}),
		//	expected: map[string]models.ReceiverPermissionSet{
		//		recv1.UID: permissions(),
		//		recv2.UID: permissions(),
		//		recv3.UID: permissions(),
		//	},
		//	expectedWithProvisioning: map[string]models.ReceiverPermissionSet{
		//		recv1.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
		//		recv2.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
		//		recv3.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
		//	},
		//},
		//{
		//	name: "legacy global provisioning writer should have full write on provisioning only",
		//	user: newViewUser(ac.Permission{Action: ac.ActionAlertingProvisioningWrite}),
		//	expected: map[string]models.ReceiverPermissionSet{
		//		recv1.UID: permissions(),
		//		recv2.UID: permissions(),
		//		recv3.UID: permissions(),
		//	},
		//	expectedWithProvisioning: map[string]models.ReceiverPermissionSet{
		//		recv1.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
		//		recv2.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
		//		recv3.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
		//	},
		//},
		// Receiver create
		{
			name: "receiver create should not have write",
			user: newEmptyUser(ac.Permission{Action: ac.ActionAlertingReceiversCreate}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		// Receiver update.
		{
			name: "global receiver update should have write but no delete",
			user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversAll}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionWrite),
				recv2.UID: permissions(models.ReceiverPermissionWrite),
				recv3.UID: permissions(models.ReceiverPermissionWrite),
			},
		},
		{
			name: "per-receiver update should have per-receiver write but no delete",
			user: newViewUser(
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionWrite),
				recv2.UID: permissions(),
				recv3.UID: permissions(models.ReceiverPermissionWrite),
			},
		},
		{
			name: "per-receiver update should require read",
			user: newEmptyUser(
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		// Receiver delete.
		{
			name: "global receiver delete should have delete but no write",
			user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversAll}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionDelete),
				recv2.UID: permissions(models.ReceiverPermissionDelete),
				recv3.UID: permissions(models.ReceiverPermissionDelete),
			},
		},
		{
			name: "per-receiver delete should have per-receiver delete but no write",
			user: newViewUser(
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionDelete),
				recv2.UID: permissions(),
				recv3.UID: permissions(models.ReceiverPermissionDelete),
			},
		},
		{
			name: "per-receiver delete should require read",
			user: newEmptyUser(
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		// Receiver admin.
		{
			name: "receiver read permissions alone can't admin",
			user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversAll}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		{
			name: "receiver write permissions alone can't admin",
			user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversAll}),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		{
			name: "global receiver read + write permissions can admin",
			user: newViewUser(
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversAll},
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversAll},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionAdmin),
				recv2.UID: permissions(models.ReceiverPermissionAdmin),
				recv3.UID: permissions(models.ReceiverPermissionAdmin),
			},
		},
		{
			name: "per-receiver read + write permissions should have per-receiver admin",
			user: newViewUser(
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionAdmin),
				recv2.UID: permissions(),
				recv3.UID: permissions(models.ReceiverPermissionAdmin),
			},
		},
		{
			name: "per-receiver admin should require read",
			user: newEmptyUser(
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(),
				recv3.UID: permissions(),
			},
		},
		// Mixed permissions.
		{
			name: "legacy provisioning secret read, receiver write",
			user: newViewUser(
				ac.Permission{Action: ac.ActionAlertingProvisioningReadSecrets},
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(models.ReceiverPermissionWrite),
				recv3.UID: permissions(),
			},
			expectedWithProvisioning: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionReadSecret),
				recv2.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionWrite),
				recv3.UID: permissions(models.ReceiverPermissionReadSecret),
			},
		},
		{
			name: "legacy provisioning secret read, receiver delete",
			user: newViewUser(
				ac.Permission{Action: ac.ActionAlertingProvisioningReadSecrets},
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(),
				recv2.UID: permissions(models.ReceiverPermissionDelete),
				recv3.UID: permissions(),
			},
			expectedWithProvisioning: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionReadSecret),
				recv2.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionDelete),
				recv3.UID: permissions(models.ReceiverPermissionReadSecret),
			},
		},
		{
			name: "legacy write, receiver secret",
			user: newViewUser(
				ac.Permission{Action: ac.ActionAlertingNotificationsWrite},
				ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
				recv2.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
				recv3.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
			},
		},
		{
			name: "mixed secret / delete / write",
			user: newViewUser(
				ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionWrite),
				recv2.UID: permissions(models.ReceiverPermissionWrite, models.ReceiverPermissionDelete),
				recv3.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionDelete),
			},
		},
		{
			name: "mixed requires read",
			user: newEmptyUser(
				ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversReadSecrets, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversUpdate, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv2.UID)},
				ac.Permission{Action: ac.ActionAlertingReceiversDelete, Scope: models.ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
			),
			expected: map[string]models.ReceiverPermissionSet{
				recv1.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionWrite),
				recv2.UID: permissions(),
				recv3.UID: permissions(models.ReceiverPermissionReadSecret, models.ReceiverPermissionDelete),
			},
		},
	}
	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			svc := NewReceiverAccess[*models.Receiver](&recordingAccessControlFake{}, false)

			actual, err := svc.Access(context.Background(), testCase.user, allReceivers...)

			assert.NoError(t, err)
			assert.Equalf(t, testCase.expected, actual, "expected: %v, actual: %v", testCase.expected, actual)

			provisioningPerms := testCase.expected
			if testCase.expectedWithProvisioning != nil {
				provisioningPerms = testCase.expectedWithProvisioning
			}
			svc = NewReceiverAccess[*models.Receiver](&recordingAccessControlFake{}, true)
			actual, err = svc.Access(context.Background(), testCase.user, allReceivers...)
			assert.NoError(t, err)
			assert.Equalf(t, provisioningPerms, actual, "expectedWithProvisioning: %v, actual: %v", provisioningPerms, actual)
		})
	}
}

func newEmptyUser(permissions ...ac.Permission) identity.Requester {
	return ac.BackgroundUser("test", orgID, org.RoleNone, permissions)
}

func newViewUser(permissions ...ac.Permission) identity.Requester {
	return ac.BackgroundUser("test", orgID, org.RoleNone, append([]ac.Permission{
		{Action: ac.ActionAlertingReceiversRead, Scope: models.ScopeReceiversAll},
		{Action: ac.ActionAlertingNotificationsRead},
	}, permissions...))
}
