package memory

import (
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/AgentTeam/internal/service/content"
	memorystore "github.com/tidbcloud/AgentTeam/internal/store/resource/memory"
	"gorm.io/gorm"
)

type memoryContentWorkspaceVerifier struct {
	allowed bool
	err     error
}

func (verifier memoryContentWorkspaceVerifier) HasWorkspaceAccess(context.Context, string, string) (bool, error) {
	return verifier.allowed, verifier.err
}

type memoryContentAccessFixture struct {
	memory memorystore.Memory
	grants map[string]memorystore.MemoryGrant
	err    error
}

func (fixture *memoryContentAccessFixture) GetMemory(context.Context, string, string) (memorystore.Memory, error) {
	if fixture.err != nil {
		return memorystore.Memory{}, fixture.err
	}
	if fixture.memory.ID == "" {
		return memorystore.Memory{}, gorm.ErrRecordNotFound
	}
	return fixture.memory, nil
}

func (fixture *memoryContentAccessFixture) GetMemoryGrant(_ context.Context, _, _, _, subject string) (memorystore.MemoryGrant, error) {
	grant, ok := fixture.grants[subject]
	if !ok {
		return memorystore.MemoryGrant{}, gorm.ErrRecordNotFound
	}
	return grant, nil
}

func TestMemoryContentAccessPolicyRechecksLiveGrantForEveryAction(t *testing.T) {
	store := &memoryContentAccessFixture{
		memory: memorystore.Memory{ID: "mem_1", CreatedBy: "usr_owner", IsActive: true},
		grants: map[string]memorystore.MemoryGrant{
			"usr_viewer":  {Role: "view", SubjectType: "user"},
			"usr_manager": {Role: "manage", SubjectType: "user"},
		},
	}
	policy, err := NewMemoryContentAccessPolicy(memoryContentWorkspaceVerifier{allowed: true}, store)
	require.NoError(t, err)

	request := memoryContentAccessRequest("usr_viewer", content.AccessReadHistory)
	require.NoError(t, policy.AuthorizeContentAccess(context.Background(), request))

	request.Action = content.AccessPublish
	request.Principal.Actions = []content.AccessAction{content.AccessPublish}
	require.ErrorIs(t, policy.AuthorizeContentAccess(context.Background(), request), content.ErrAccessDenied)

	request.Principal.Subject = "usr_manager"
	require.NoError(t, policy.AuthorizeContentAccess(context.Background(), request))

	delete(store.grants, "usr_manager")
	require.ErrorIs(t, policy.AuthorizeContentAccess(context.Background(), request), content.ErrAccessDenied)
}

func TestMemoryContentAccessPolicyFailsClosedForScopeMembershipAndStoreErrors(t *testing.T) {
	store := &memoryContentAccessFixture{memory: memorystore.Memory{ID: "mem_1", CreatedBy: "usr_owner", IsActive: true}}
	policy, err := NewMemoryContentAccessPolicy(memoryContentWorkspaceVerifier{allowed: false}, store)
	require.NoError(t, err)
	require.ErrorIs(t, policy.AuthorizeContentAccess(context.Background(), memoryContentAccessRequest("usr_owner", content.AccessReadPayload)), content.ErrAccessDenied)

	policy, err = NewMemoryContentAccessPolicy(memoryContentWorkspaceVerifier{allowed: true}, store)
	require.NoError(t, err)
	wrongDomain := memoryContentAccessRequest("usr_owner", content.AccessReadPayload)
	wrongDomain.Domain = "skill"
	require.ErrorIs(t, policy.AuthorizeContentAccess(context.Background(), wrongDomain), content.ErrAccessDenied)

	store.err = errors.New("database unavailable")
	err = policy.AuthorizeContentAccess(context.Background(), memoryContentAccessRequest("usr_owner", content.AccessReadPayload))
	require.ErrorContains(t, err, "load memory content owner")
}

func memoryContentAccessRequest(subject string, action content.AccessAction) content.AccessRequest {
	return content.AccessRequest{
		Principal: content.AccessPrincipal{
			Subject: "" + subject, WorkspaceID: "wsp_1", Domain: memoryContentDomain,
			BindingKey: "mem_1", ContentID: "ctn_1", Actions: []content.AccessAction{action},
		},
		WorkspaceID: "wsp_1", Domain: memoryContentDomain, BindingKey: "mem_1", ContentID: "ctn_1", Action: action,
	}
}
