package resourcepermission

import (
	"context"
	"encoding/json"
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
	"github.com/grafana/grafana/pkg/apimachinery/utils"
	"github.com/grafana/grafana/pkg/infra/db"
	"github.com/grafana/grafana/pkg/storage/legacysql"
	"github.com/grafana/grafana/pkg/storage/unified/resource"
	"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
	"github.com/grafana/grafana/pkg/util/testutil"
)

func TestIntegration_ResourcePermSqlBackend_ReadResource(t *testing.T) {
	testutil.SkipIntegrationTestInShortMode(t)

	backend := setupBackend(t)
	sql, err := backend.dbProvider(context.Background())
	require.NoError(t, err)
	setupTestRoles(t, sql.DB)

	updated1 := time.Date(2025, 9, 2, 0, 0, 0, 0, time.UTC)
	updated2 := time.Date(2025, 9, 3, 0, 0, 0, 0, time.UTC) // managed role for team 1 has a later updated permission

	t.Run("ReadResource - Invalid namespace", func(t *testing.T) {
		resp := backend.ReadResource(context.Background(), &resourcepb.ReadRequest{
			Key: &resourcepb.ResourceKey{Name: "folder.grafana.app-folders-fold1", Namespace: "invalid"},
		})
		require.NotNil(t, resp)
		require.NotNil(t, resp.Error)
		require.Contains(t, resp.Error.Message, errInvalidNamespace.Error())
		require.Equal(t, int32(400), resp.Error.Code)
	})

	t.Run("ReadResource - Get fold1 resource permissions", func(t *testing.T) {
		resp := backend.ReadResource(context.Background(), &resourcepb.ReadRequest{
			Key: &resourcepb.ResourceKey{Name: "folder.grafana.app-folders-fold1", Namespace: "default"},
		})

		require.NotNil(t, resp)
		require.Nil(t, resp.Error)
		require.NotNil(t, resp.Value)
		require.Equal(t, updated1.UnixMilli(), resp.ResourceVersion)

		var permission v0alpha1.ResourcePermission
		err := json.Unmarshal(resp.Value, &permission)
		require.NoError(t, err)
		require.Equal(t, "folder.grafana.app-folders-fold1", permission.Name)
		require.Len(t, permission.Spec.Permissions, 1)
		require.Equal(t, "user-1", permission.Spec.Permissions[0].Name)
		require.Equal(t, v0alpha1.ResourcePermissionSpecPermissionKindUser, permission.Spec.Permissions[0].Kind)
		require.Equal(t, "view", permission.Spec.Permissions[0].Verb)
	})

	t.Run("ReadResource - Get fold1 in org-2 resource permissions", func(t *testing.T) {
		resp := backend.ReadResource(context.Background(), &resourcepb.ReadRequest{
			Key: &resourcepb.ResourceKey{Name: "folder.grafana.app-folders-fold1", Namespace: "org-2"},
		})

		require.NotNil(t, resp)
		require.Nil(t, resp.Error)
		require.NotNil(t, resp.Value)
		require.Equal(t, updated1.UnixMilli(), resp.ResourceVersion)

		var permission v0alpha1.ResourcePermission
		err := json.Unmarshal(resp.Value, &permission)
		require.NoError(t, err)
		require.Equal(t, "folder.grafana.app-folders-fold1", permission.Name)
		require.Len(t, permission.Spec.Permissions, 1)
		require.Equal(t, "user-2", permission.Spec.Permissions[0].Name)
		require.Equal(t, v0alpha1.ResourcePermissionSpecPermissionKindUser, permission.Spec.Permissions[0].Kind)
		require.Equal(t, "edit", permission.Spec.Permissions[0].Verb)
	})

	t.Run("ReadResource - Get dash1 resource permissions", func(t *testing.T) {
		resp := backend.ReadResource(context.Background(), &resourcepb.ReadRequest{
			Key: &resourcepb.ResourceKey{Name: "dashboard.grafana.app-dashboards-dash1", Namespace: "default"},
		})

		require.NotNil(t, resp)
		require.Nil(t, resp.Error)
		require.NotNil(t, resp.Value)
		require.Equal(t, updated2.UnixMilli(), resp.ResourceVersion)

		var permission v0alpha1.ResourcePermission
		err := json.Unmarshal(resp.Value, &permission)
		require.NoError(t, err)
		require.Equal(t, "dashboard.grafana.app-dashboards-dash1", permission.Name)
		require.Len(t, permission.Spec.Permissions, 4)

		require.Equal(t, "Editor", permission.Spec.Permissions[0].Name)
		require.Equal(t, v0alpha1.ResourcePermissionSpecPermissionKindBasicRole, permission.Spec.Permissions[0].Kind)
		require.Equal(t, "edit", permission.Spec.Permissions[0].Verb)

		require.Equal(t, "sa-1", permission.Spec.Permissions[1].Name)
		require.Equal(t, v0alpha1.ResourcePermissionSpecPermissionKindServiceAccount, permission.Spec.Permissions[1].Kind)
		require.Equal(t, "view", permission.Spec.Permissions[1].Verb)

		require.Equal(t, "team-1", permission.Spec.Permissions[2].Name)
		require.Equal(t, v0alpha1.ResourcePermissionSpecPermissionKindTeam, permission.Spec.Permissions[2].Kind)
		require.Equal(t, "admin", permission.Spec.Permissions[2].Verb)

		require.Equal(t, "user-1", permission.Spec.Permissions[3].Name)
		require.Equal(t, v0alpha1.ResourcePermissionSpecPermissionKindUser, permission.Spec.Permissions[3].Kind)
		require.Equal(t, "edit", permission.Spec.Permissions[3].Verb)
	})
}

func TestWriteEvent_Add(t *testing.T) {
	store := db.InitTestDB(t)

	timeNow = func() time.Time {
		return time.Date(2025, 8, 28, 17, 13, 0, 0, time.UTC)
	}

	sqlHelper := &legacysql.LegacyDatabaseHelper{
		DB:    store,
		Table: func(name string) string { return name },
	}

	dbProvider := func(ctx context.Context) (*legacysql.LegacyDatabaseHelper, error) {
		return sqlHelper, nil
	}

	t.Run("should error with invalid namespace", func(t *testing.T) {
		backend := ProvideStorageBackend(dbProvider, NewMappersRegistry())

		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type: resourcepb.WatchEvent_ADDED,
			Key:  &resourcepb.ResourceKey{Name: "folder.grafana.app-folders-fold1", Namespace: "invalid"},
		})

		require.Zero(t, rv)
		require.NotNil(t, err)
		require.Contains(t, err.Error(), "requires a valid namespace")
	})

	t.Run("should error if resource name is empty", func(t *testing.T) {
		backend := ProvideStorageBackend(dbProvider, NewMappersRegistry())

		resourcePerm, err := utils.MetaAccessor(&v0alpha1.ResourcePermission{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "folder.grafana.app-folders-",
				Namespace: "default",
			},
			Spec: v0alpha1.ResourcePermissionSpec{
				Resource: v0alpha1.ResourcePermissionspecResource{
					ApiGroup: "folder.grafana.app",
					Resource: "folders",
					Name:     "",
				},
				Permissions: []v0alpha1.ResourcePermissionspecPermission{
					{
						Kind: v0alpha1.ResourcePermissionSpecPermissionKindBasicRole,
						Name: "Viewer",
						Verb: "Admin",
					},
				},
			},
		})
		require.NoError(t, err)

		gr := v0alpha1.ResourcePermissionInfo.GroupResource()
		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type:   resourcepb.WatchEvent_ADDED,
			Key:    &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "folder.grafana.app-folders-", Namespace: "default"},
			Object: resourcePerm,
		})
		require.Zero(t, rv)
		require.NotNil(t, err)
		require.Contains(t, err.Error(), errInvalidName.Error())
	})

	t.Run("should error if the resource is unknown", func(t *testing.T) {
		backend := ProvideStorageBackend(dbProvider, NewMappersRegistry())

		resourcePerm, err := utils.MetaAccessor(&v0alpha1.ResourcePermission{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "unknown.grafana.app-unknown-ukn1",
				Namespace: "default",
			},
			Spec: v0alpha1.ResourcePermissionSpec{
				Resource: v0alpha1.ResourcePermissionspecResource{
					ApiGroup: "unknown.grafana.app",
					Resource: "unknown",
					Name:     "ukn1",
				},
				Permissions: []v0alpha1.ResourcePermissionspecPermission{
					{
						Kind: v0alpha1.ResourcePermissionSpecPermissionKindBasicRole,
						Name: "Viewer",
						Verb: "Admin",
					},
				},
			},
		})
		require.NoError(t, err)

		gr := v0alpha1.ResourcePermissionInfo.GroupResource()
		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type:   resourcepb.WatchEvent_ADDED,
			Key:    &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "unknown.grafana.app-unknown-ukn1", Namespace: "default"},
			Object: resourcePerm,
		})
		require.Zero(t, rv)
		require.NotNil(t, err)
		require.Contains(t, err.Error(), errUnknownGroupResource.Error())
	})

	t.Run("should work with valid resource permission", func(t *testing.T) {
		backend := ProvideStorageBackend(dbProvider, NewMappersRegistry())
		backend.identityStore = NewFakeIdentityStore(t)

		resourcePerm, err := utils.MetaAccessor(&v0alpha1.ResourcePermission{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "folder.grafana.app-folders-fold1",
				Namespace: "default",
			},
			Spec: v0alpha1.ResourcePermissionSpec{
				Resource: v0alpha1.ResourcePermissionspecResource{
					ApiGroup: "folder.grafana.app",
					Resource: "folders",
					Name:     "fold1",
				},
				Permissions: []v0alpha1.ResourcePermissionspecPermission{
					{
						Kind: v0alpha1.ResourcePermissionSpecPermissionKindBasicRole,
						Name: "Viewer",
						Verb: "Admin",
					},
				},
			},
		})
		require.NoError(t, err)

		gr := v0alpha1.ResourcePermissionInfo.GroupResource()
		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type:   resourcepb.WatchEvent_ADDED,
			Key:    &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "folder.grafana.app-folders-fold1", Namespace: "default"},
			Object: resourcePerm,
		})

		require.NoError(t, err)
		require.Equal(t, timeNow().UnixMilli(), rv)

		t.Run("Should error on duplicate add", func(t *testing.T) {
			rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
				Type:   resourcepb.WatchEvent_ADDED,
				Key:    &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "folder.grafana.app-folders-fold1", Namespace: "default"},
				Object: resourcePerm,
			})

			require.NotNil(t, err)
			require.Contains(t, err.Error(), errConflict.Error())
			require.Zero(t, rv)
		})
	})
}

func TestIntegration_ResourcePermSqlBackend_ListIterator(t *testing.T) {
	testutil.SkipIntegrationTestInShortMode(t)

	// No result => list resource version should be current time
	now := time.Date(2025, 9, 9, 0, 0, 0, 0, time.UTC)
	timeNow = func() time.Time {
		return now
	}

	backend := setupBackend(t)
	sql, err := backend.dbProvider(context.Background())
	require.NoError(t, err)
	setupTestRoles(t, sql.DB)

	gr := v0alpha1.ResourcePermissionInfo.GroupResource()

	t.Run("Should error with invalid namespace", func(t *testing.T) {
		req := &resourcepb.ListRequest{
			Options: &resourcepb.ListOptions{
				Key: &resourcepb.ResourceKey{
					Group:     gr.Group,
					Resource:  gr.Resource,
					Namespace: "invalid",
				},
			},
			Limit: 10,
		}

		callbackCalled := false
		count, err := backend.ListIterator(context.Background(), req, func(resource.ListIterator) error {
			callbackCalled = true
			return nil
		})

		require.Error(t, err)
		require.Contains(t, err.Error(), "invalid namespace")
		require.Zero(t, count)
		require.False(t, callbackCalled)
	})

	t.Run("Should error with invalid continue token", func(t *testing.T) {
		req := &resourcepb.ListRequest{
			Options: &resourcepb.ListOptions{
				Key: &resourcepb.ResourceKey{
					Group:     gr.Group,
					Resource:  gr.Resource,
					Namespace: "default",
				},
			},
			NextPageToken: "invalid-token",
			Limit:         10,
		}

		callbackCalled := false
		count, err := backend.ListIterator(context.Background(), req, func(resource.ListIterator) error {
			callbackCalled = true
			return nil
		})

		require.Error(t, err)
		require.Contains(t, err.Error(), "invalid continue token")
		require.Zero(t, count)
		require.False(t, callbackCalled)
	})

	t.Run("Should list all resources for org-1", func(t *testing.T) {
		req := &resourcepb.ListRequest{
			Options: &resourcepb.ListOptions{
				Key: &resourcepb.ResourceKey{
					Group:     gr.Group,
					Resource:  gr.Resource,
					Namespace: "default",
				},
			},
			Limit: 100,
		}

		var results []v0alpha1.ResourcePermission
		listRV, err := backend.ListIterator(context.Background(), req, func(it resource.ListIterator) error {
			for it.Next() {
				var perm v0alpha1.ResourcePermission
				err := json.Unmarshal(it.Value(), &perm)
				require.NoError(t, err)
				results = append(results, perm)

				// Test iterator methods
				require.NotEmpty(t, it.Name())
				require.Equal(t, "default", it.Namespace())
				require.NotZero(t, it.ResourceVersion())
				require.Empty(t, it.Folder()) // ResourcePermissions don't have folders
			}
			return it.Error()
		})

		require.NoError(t, err)
		require.Equal(t, updated.UnixMilli(), listRV)
		require.Len(t, results, 2)

		// Results should be sorted by scope (alphabetically)
		require.Equal(t, "dashboard.grafana.app-dashboards-dash1", results[0].Name)
		require.Equal(t, "folder.grafana.app-folders-fold1", results[1].Name)

		// Verify dash1 permissions
		require.Len(t, results[0].Spec.Permissions, 4)
		require.Equal(t, "dashboard.grafana.app", results[0].Spec.Resource.ApiGroup)
		require.Equal(t, "dashboards", results[0].Spec.Resource.Resource)
		require.Equal(t, "dash1", results[0].Spec.Resource.Name)

		// Verify fold1 permissions
		require.Len(t, results[1].Spec.Permissions, 1)
		require.Equal(t, "folder.grafana.app", results[1].Spec.Resource.ApiGroup)
		require.Equal(t, "folders", results[1].Spec.Resource.Resource)
		require.Equal(t, "fold1", results[1].Spec.Resource.Name)
	})

	t.Run("Should handle pagination with continue token", func(t *testing.T) {
		req := &resourcepb.ListRequest{
			Options: &resourcepb.ListOptions{
				Key: &resourcepb.ResourceKey{
					Group:     gr.Group,
					Resource:  gr.Resource,
					Namespace: "default",
				},
			},
			Limit:         1,
			NextPageToken: "start:1", // Skip first item
		}

		var results []v0alpha1.ResourcePermission
		listRV, err := backend.ListIterator(context.Background(), req, func(it resource.ListIterator) error {
			for it.Next() {
				var perm v0alpha1.ResourcePermission
				err := json.Unmarshal(it.Value(), &perm)
				require.NoError(t, err)
				results = append(results, perm)

				// Test continue token
				require.NotEmpty(t, it.ContinueToken())
			}
			return it.Error()
		})

		require.NoError(t, err)
		require.Equal(t, updated.UnixMilli(), listRV) // Only fold1
		require.Len(t, results, 1)
		require.Equal(t, "folder.grafana.app-folders-fold1", results[0].Name)
	})

	t.Run("Should use default limit when not specified", func(t *testing.T) {
		req := &resourcepb.ListRequest{
			Options: &resourcepb.ListOptions{
				Key: &resourcepb.ResourceKey{
					Group:     gr.Group,
					Resource:  gr.Resource,
					Namespace: "default",
				},
			},
			// No limit specified, should default to 50
		}

		callbackCalled := false
		listRV, err := backend.ListIterator(context.Background(), req, func(it resource.ListIterator) error {
			callbackCalled = true
			// Just verify the iterator works
			for it.Next() {
				require.NotNil(t, it.Value())
			}
			return it.Error()
		})

		require.NoError(t, err)
		require.True(t, callbackCalled)
		require.Equal(t, updated.UnixMilli(), listRV)
	})

	t.Run("Should return empty results for org with no permissions", func(t *testing.T) {
		req := &resourcepb.ListRequest{
			Options: &resourcepb.ListOptions{
				Key: &resourcepb.ResourceKey{
					Group:     gr.Group,
					Resource:  gr.Resource,
					Namespace: "org-999", // Non-existent org
				},
			},
			Limit: 10,
		}

		var results []v0alpha1.ResourcePermission
		listRV, err := backend.ListIterator(context.Background(), req, func(it resource.ListIterator) error {
			for it.Next() {
				var perm v0alpha1.ResourcePermission
				err := json.Unmarshal(it.Value(), &perm)
				require.NoError(t, err)
				results = append(results, perm)
			}
			return it.Error()
		})

		require.NoError(t, err)
		require.Equal(t, listRV, now.UnixMilli())
		require.Empty(t, results)
	})

	t.Run("Should propagate callback errors", func(t *testing.T) {
		req := &resourcepb.ListRequest{
			Options: &resourcepb.ListOptions{
				Key: &resourcepb.ResourceKey{
					Group:     gr.Group,
					Resource:  gr.Resource,
					Namespace: "default",
				},
			},
			Limit: 10,
		}

		expectedErr := errors.New("callback error")
		listRV, err := backend.ListIterator(context.Background(), req, func(it resource.ListIterator) error {
			return expectedErr
		})

		require.Error(t, err)
		require.ErrorAs(t, err, &expectedErr)
		require.Zero(t, listRV)
	})

	t.Run("Should paginate through all results", func(t *testing.T) {
		expectedResults := []string{
			"dashboard.grafana.app-dashboards-dash1",
			"folder.grafana.app-folders-fold1",
		}
		req := &resourcepb.ListRequest{
			Options: &resourcepb.ListOptions{
				Key: &resourcepb.ResourceKey{
					Group:     gr.Group,
					Resource:  gr.Resource,
					Namespace: "default",
				},
			},
			Limit:         1,
			NextPageToken: "start:0",
		}

		var perm v0alpha1.ResourcePermission
		continueToken := "start:0"
		results := []string{}
		_, _ = backend.ListIterator(context.Background(), req, func(it resource.ListIterator) error {
			for it.Next() {
				continueToken = it.ContinueToken()
				require.NoError(t, json.Unmarshal(it.Value(), &perm))
				results = append(results, perm.Name)
				if len(results) >= int(req.Limit) {
					return nil
				}
			}
			return nil
		})
		require.Equal(t, "start:1", continueToken)

		req.NextPageToken = continueToken
		_, _ = backend.ListIterator(context.Background(), req, func(it resource.ListIterator) error {
			for it.Next() {
				continueToken = it.ContinueToken()
				require.NoError(t, json.Unmarshal(it.Value(), &perm))
				results = append(results, perm.Name)
				if len(results) >= int(req.Limit) {
					return nil
				}
			}
			return nil
		})
		require.Equal(t, "start:2", continueToken)

		// No more results, token should not change
		req.NextPageToken = continueToken
		_, _ = backend.ListIterator(context.Background(), req, func(it resource.ListIterator) error {
			for it.Next() {
				continueToken = it.ContinueToken()
				require.NoError(t, json.Unmarshal(it.Value(), &perm))
				results = append(results, perm.Name)
				if len(results) >= int(req.Limit) {
					return nil
				}
			}
			return nil
		})
		require.Equal(t, "start:2", continueToken) // No change

		// Verify we got all expected results
		require.Len(t, results, 2)
		require.ElementsMatch(t, expectedResults, results)
	})
}

func TestIntegration_WriteEvent_Delete(t *testing.T) {
	testutil.SkipIntegrationTestInShortMode(t)

	backend := setupBackend(t)
	sql, err := backend.dbProvider(context.Background())
	require.NoError(t, err)
	setupTestRoles(t, sql.DB)

	updated1 := time.Date(2025, 9, 2, 0, 0, 0, 0, time.UTC)
	updated2 := time.Date(2025, 9, 3, 0, 0, 0, 0, time.UTC) // managed role for team 1 has a later updated permission

	gr := v0alpha1.ResourcePermissionInfo.GroupResource()

	t.Run("Should error if namespace is invalid", func(t *testing.T) {
		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Key:  &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "folder.grafana.app-folders-fold1", Namespace: "invalid"},
			Type: resourcepb.WatchEvent_DELETED,
		})

		require.Zero(t, rv)
		require.NotNil(t, err)
		require.Contains(t, err.Error(), "requires a valid namespace")
	})

	t.Run("Should fail to delete resource permissions if resource name is not specified", func(t *testing.T) {
		_, err = backend.WriteEvent(context.Background(), resource.WriteEvent{
			Key:  &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "", Namespace: "default"},
			Type: resourcepb.WatchEvent_DELETED,
		})
		require.NotNil(t, err)
		require.Contains(t, err.Error(), "invalid key")
	})

	t.Run("Should fail to delete resource permissions for unknown resource", func(t *testing.T) {
		_, err = backend.WriteEvent(context.Background(), resource.WriteEvent{
			Key:  &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "unknown.grafana.app-unknown-uid", Namespace: "default"},
			Type: resourcepb.WatchEvent_DELETED,
		})
		require.NotNil(t, err)
		require.Contains(t, err.Error(), "unknown group/resource")
	})

	t.Run("Should successfully delete fold1 permissions in org-1", func(t *testing.T) {
		// Check that permissions exist
		resp := backend.ReadResource(context.Background(), &resourcepb.ReadRequest{
			Key: &resourcepb.ResourceKey{Name: "folder.grafana.app-folders-fold1", Namespace: "default"},
		})

		require.NotNil(t, resp)
		require.Nil(t, resp.Error)
		require.NotNil(t, resp.Value)

		var permission v0alpha1.ResourcePermission
		err := json.Unmarshal(resp.Value, &permission)
		require.NoError(t, err)
		require.Len(t, permission.Spec.Permissions, 1)

		_, err = backend.WriteEvent(context.Background(), resource.WriteEvent{
			Key:  &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "folder.grafana.app-folders-fold1", Namespace: "default"},
			Type: resourcepb.WatchEvent_DELETED,
		})
		require.Nil(t, err)

		// Check that permissions are deleted
		resp = backend.ReadResource(context.Background(), &resourcepb.ReadRequest{
			Key: &resourcepb.ResourceKey{Name: "folder.grafana.app-folders-fold1", Namespace: "default"},
		})
		require.NotNil(t, resp.Error)

		// Check that org-2 permissions are unaffected
		resp = backend.ReadResource(context.Background(), &resourcepb.ReadRequest{
			Key: &resourcepb.ResourceKey{Name: "folder.grafana.app-folders-fold1", Namespace: "org-2"},
		})
		require.NotNil(t, resp)
		require.Nil(t, resp.Error)
		require.NotNil(t, resp.Value)
		require.Equal(t, updated1.UnixMilli(), resp.ResourceVersion)
		err = json.Unmarshal(resp.Value, &permission)
		require.NoError(t, err)
		require.Len(t, permission.Spec.Permissions, 1)

		// Check that dash1 permissions in org 1 are unaffected
		resp = backend.ReadResource(context.Background(), &resourcepb.ReadRequest{
			Key: &resourcepb.ResourceKey{Name: "dashboard.grafana.app-dashboards-dash1", Namespace: "default"},
		})
		require.NotNil(t, resp)
		require.Nil(t, resp.Error)
		require.NotNil(t, resp.Value)
		require.Equal(t, updated2.UnixMilli(), resp.ResourceVersion)
		err = json.Unmarshal(resp.Value, &permission)
		require.NoError(t, err)
		require.Len(t, permission.Spec.Permissions, 4)
	})
}

func TestWriteEvent_Modify(t *testing.T) {
	store := db.InitTestDB(t)

	timeNow = func() time.Time {
		return time.Date(2025, 8, 28, 17, 13, 0, 0, time.UTC)
	}

	sqlHelper := &legacysql.LegacyDatabaseHelper{
		DB:    store,
		Table: func(name string) string { return name },
	}

	dbProvider := func(ctx context.Context) (*legacysql.LegacyDatabaseHelper, error) {
		return sqlHelper, nil
	}

	t.Run("should error with invalid namespace", func(t *testing.T) {
		backend := ProvideStorageBackend(dbProvider, NewMappersRegistry())

		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type: resourcepb.WatchEvent_MODIFIED,
			Key:  &resourcepb.ResourceKey{Name: "folder.grafana.app-folders-fold1", Namespace: "invalid"},
		})

		require.Zero(t, rv)
		require.NotNil(t, err)
		require.Contains(t, err.Error(), "requires a valid namespace")
	})

	t.Run("should error if resource name is empty", func(t *testing.T) {
		backend := ProvideStorageBackend(dbProvider, NewMappersRegistry())

		resourcePerm, err := utils.MetaAccessor(&v0alpha1.ResourcePermission{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "folder.grafana.app-folders-",
				Namespace: "default",
			},
			Spec: v0alpha1.ResourcePermissionSpec{
				Resource: v0alpha1.ResourcePermissionspecResource{
					ApiGroup: "folder.grafana.app",
					Resource: "folders",
					Name:     "",
				},
				Permissions: []v0alpha1.ResourcePermissionspecPermission{
					{
						Kind: v0alpha1.ResourcePermissionSpecPermissionKindBasicRole,
						Name: "Viewer",
						Verb: "Admin",
					},
				},
			},
		})
		require.NoError(t, err)

		gr := v0alpha1.ResourcePermissionInfo.GroupResource()
		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type:   resourcepb.WatchEvent_MODIFIED,
			Key:    &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "folder.grafana.app-folders-", Namespace: "default"},
			Object: resourcePerm,
		})
		require.Zero(t, rv)
		require.NotNil(t, err)
		require.Contains(t, err.Error(), errInvalidName.Error())
	})

	t.Run("should error if the resource is unknown", func(t *testing.T) {
		backend := ProvideStorageBackend(dbProvider, NewMappersRegistry())

		resourcePerm, err := utils.MetaAccessor(&v0alpha1.ResourcePermission{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "unknown.grafana.app-unknown-ukn1",
				Namespace: "default",
			},
			Spec: v0alpha1.ResourcePermissionSpec{
				Resource: v0alpha1.ResourcePermissionspecResource{
					ApiGroup: "unknown.grafana.app",
					Resource: "unknown",
					Name:     "ukn1",
				},
				Permissions: []v0alpha1.ResourcePermissionspecPermission{
					{
						Kind: v0alpha1.ResourcePermissionSpecPermissionKindBasicRole,
						Name: "Viewer",
						Verb: "Admin",
					},
				},
			},
		})
		require.NoError(t, err)

		gr := v0alpha1.ResourcePermissionInfo.GroupResource()
		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type:   resourcepb.WatchEvent_MODIFIED,
			Key:    &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "unknown.grafana.app-unknown-ukn1", Namespace: "default"},
			Object: resourcePerm,
		})
		require.Zero(t, rv)
		require.NotNil(t, err)
		require.Contains(t, err.Error(), errUnknownGroupResource.Error())
	})

	t.Run("should work with valid resource permission", func(t *testing.T) {
		backend := ProvideStorageBackend(dbProvider, NewMappersRegistry())
		backend.identityStore = NewFakeIdentityStore(t)

		resourcePerm, err := utils.MetaAccessor(&v0alpha1.ResourcePermission{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "folder.grafana.app-folders-fold1",
				Namespace: "default",
			},
			Spec: v0alpha1.ResourcePermissionSpec{
				Resource: v0alpha1.ResourcePermissionspecResource{
					ApiGroup: "folder.grafana.app",
					Resource: "folders",
					Name:     "fold1",
				},
				Permissions: []v0alpha1.ResourcePermissionspecPermission{
					{
						Kind: v0alpha1.ResourcePermissionSpecPermissionKindBasicRole,
						Name: "Viewer",
						Verb: "Admin",
					},
				},
			},
		})
		require.NoError(t, err)

		// Create resource first
		gr := v0alpha1.ResourcePermissionInfo.GroupResource()
		rv, err := backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type:   resourcepb.WatchEvent_ADDED,
			Key:    &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "folder.grafana.app-folders-fold1", Namespace: "default"},
			Object: resourcePerm,
		})

		require.NoError(t, err)
		require.Equal(t, timeNow().UnixMilli(), rv)

		// Modify resource
		resourcePerm, err = utils.MetaAccessor(&v0alpha1.ResourcePermission{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "folder.grafana.app-folders-fold1",
				Namespace: "default",
			},
			Spec: v0alpha1.ResourcePermissionSpec{
				Resource: v0alpha1.ResourcePermissionspecResource{
					ApiGroup: "folder.grafana.app",
					Resource: "folders",
					Name:     "fold1",
				},
				Permissions: []v0alpha1.ResourcePermissionspecPermission{
					{
						Kind: v0alpha1.ResourcePermissionSpecPermissionKindBasicRole,
						Name: "Viewer",
						Verb: "Edit",
					},
				},
			},
		})
		require.NoError(t, err)

		rv, err = backend.WriteEvent(context.Background(), resource.WriteEvent{
			Type:   resourcepb.WatchEvent_MODIFIED,
			Key:    &resourcepb.ResourceKey{Group: gr.Group, Resource: gr.Resource, Name: "folder.grafana.app-folders-fold1", Namespace: "default"},
			Object: resourcePerm,
		})

		require.NoError(t, err)
		require.Equal(t, timeNow().UnixMilli(), rv)
	})
}
