package roles

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/AgentTeam/internal/service/content"
	rolestore "github.com/tidbcloud/AgentTeam/internal/store/resource/roles"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func TestRoleContentAccessPolicyRechecksLiveWorkspaceAndDomainGrant(t *testing.T) {
	ctx := context.Background()
	db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(&rolestore.Role{}, &rolestore.RoleGrant{}))
	now := time.Now().UTC()
	require.NoError(t, db.Create(&rolestore.Role{
		ID: "role_1", WorkspaceID: "wsp_1", Slug: "reviewer", Name: "Reviewer", Description: "review",
		TagsJSON: "[]", Definition: "review", RuntimeHintsJSON: "{}", AttachmentsJSON: "[]",
		Visibility: RoleVisibilityPrivate, CreatedBy: "usr_owner", UpdatedBy: "usr_owner", CreatedAt: now, UpdatedAt: now,
	}).Error)
	require.NoError(t, db.Create(&rolestore.RoleGrant{
		ID: "rgr_view", WorkspaceID: "wsp_1", RoleID: "role_1", SubjectType: "user", SubjectID: "usr_viewer", Role: roleGrantView,
		CreatedBy: "usr_owner", UpdatedBy: "usr_owner", CreatedAt: now, UpdatedAt: now,
	}).Error)
	workspace := roleContentWorkspaceAccess{allowed: map[string]bool{
		"wsp_1\x00usr_owner":  true,
		"wsp_1\x00usr_viewer": true,
	}}
	policy, err := NewRoleContentAccessPolicy(workspace, rolestore.New(db))
	require.NoError(t, err)
	request := func(subject string, action content.AccessAction) content.AccessRequest {
		principal := content.AccessPrincipal{
			Subject: subject, WorkspaceID: "wsp_1", Domain: rolePackageContentDomain,
			BindingKey: "role_1", ContentID: "ctn_1", Generation: 2, Actions: []content.AccessAction{action},
		}
		return content.AccessRequest{
			Principal: principal, WorkspaceID: "wsp_1", Domain: rolePackageContentDomain,
			BindingKey: "role_1", ContentID: "ctn_1", Generation: 2, Action: action,
		}
	}

	require.NoError(t, policy.AuthorizeContentAccess(ctx, request("usr_owner", content.AccessPublish)))
	require.NoError(t, policy.AuthorizeContentAccess(ctx, request("usr_viewer", content.AccessReadPayload)))
	require.ErrorIs(t, policy.AuthorizeContentAccess(ctx, request("usr_viewer", content.AccessPublish)), content.ErrAccessDenied)

	for name, mutate := range map[string]func(*content.AccessRequest){
		"forged workspace": func(row *content.AccessRequest) { row.Principal.WorkspaceID = "wsp_other" },
		"wrong domain":     func(row *content.AccessRequest) { row.Domain = "skill" },
		"wrong binding":    func(row *content.AccessRequest) { row.Principal.BindingKey = "role_other" },
		"wrong content":    func(row *content.AccessRequest) { row.Principal.ContentID = "ctn_other" },
		"wrong action": func(row *content.AccessRequest) {
			row.Action = content.AccessAction("unknown")
		},
	} {
		t.Run(name, func(t *testing.T) {
			row := request("usr_owner", content.AccessReadPayload)
			mutate(&row)
			require.ErrorIs(t, policy.AuthorizeContentAccess(ctx, row), content.ErrAccessDenied)
		})
	}

	workspace.allowed["wsp_1\x00usr_owner"] = false
	require.ErrorIs(t, policy.AuthorizeContentAccess(ctx, request("usr_owner", content.AccessReadPayload)), content.ErrAccessDenied)
}
