package records

import (
	"context"
	"net/http"
	"strings"
	"testing"

	eventFeature "github.com/nexuscrm/events"
	apperrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/formula"
	"github.com/nexuscrm/kernel/pkg/recordlocks"
	"github.com/nexuscrm/kernel/pkg/validation"
	metadataFeature "github.com/nexuscrm/metadata"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/contract"
	sharedEvents "github.com/nexuscrm/shared/pkg/events"
	"github.com/nexuscrm/shared/pkg/models"
)

type permissionResourceLockRepo struct {
	stubUpsertRecordRepo
	rows                map[string]models.SObject
	applyUpdates        bool
	insertCalls         int
	updateCalls         int
	updateIDs           []string
	updates             []models.SObject
	deleteCalls         int
	bulkInsertCalls     int
	bulkInsertErrs      []error
	lockCalls           []string
	physicalDeleteCalls int
}

func (r *permissionResourceLockRepo) FindOne(_ context.Context, table string, id string) (models.SObject, error) {
	if r.rows == nil {
		return nil, nil
	}
	return cloneSObject(r.rows[strings.ToLower(table)+":"+id]), nil
}

func (r *permissionResourceLockRepo) FindAny(ctx context.Context, table string, id string) (models.SObject, error) {
	return r.FindOne(ctx, table, id)
}

func (r *permissionResourceLockRepo) GetLock(ctx context.Context, table string, id string) (models.SObject, error) {
	r.lockCalls = append(r.lockCalls, table+":"+id)
	return r.FindOne(ctx, table, id)
}

func (r *permissionResourceLockRepo) Insert(_ context.Context, table string, record models.SObject) error {
	r.insertCalls++
	if r.applyUpdates && r.rows != nil {
		id, _ := record[constants.FieldID].(string)
		if id != "" {
			r.rows[strings.ToLower(table)+":"+id] = cloneSObject(record)
		}
	}
	return nil
}

func (r *permissionResourceLockRepo) Update(_ context.Context, table string, id string, updates models.SObject) error {
	r.updateCalls++
	r.updateIDs = append(r.updateIDs, id)
	r.updates = append(r.updates, cloneSObject(updates))
	if r.applyUpdates && r.rows != nil {
		key := strings.ToLower(table) + ":" + id
		if row := r.rows[key]; row != nil {
			for fieldName, value := range updates {
				row[fieldName] = value
			}
		}
	}
	return nil
}

func (r *permissionResourceLockRepo) FindMany(_ context.Context, table string, ids []string) (map[string]models.SObject, error) {
	rows := make(map[string]models.SObject, len(ids))
	for _, id := range ids {
		if row := r.rows[strings.ToLower(table)+":"+id]; row != nil {
			rows[id] = cloneSObject(row)
		}
	}
	return rows, nil
}

func (r *permissionResourceLockRepo) Delete(_ context.Context, _ string, _ string) error {
	r.deleteCalls++
	return nil
}

func (r *permissionResourceLockRepo) PhysicalDelete(_ context.Context, table string, id string) error {
	r.physicalDeleteCalls++
	if r.applyUpdates && r.rows != nil {
		delete(r.rows, strings.ToLower(table)+":"+id)
	}
	return nil
}

func (r *permissionResourceLockRepo) BulkInsert(_ context.Context, _ string, _ []models.SObject, _ []string, _ interface{}) error {
	r.bulkInsertCalls++
	if len(r.bulkInsertErrs) > 0 {
		err := r.bulkInsertErrs[0]
		r.bulkInsertErrs = r.bulkInsertErrs[1:]
		return err
	}
	return nil
}

func (r *permissionResourceLockRepo) BulkInsertWithLanding(_ context.Context, table string, records []models.SObject, _ []string, _ interface{}) ([]bool, error) {
	r.bulkInsertCalls++
	if len(r.bulkInsertErrs) > 0 {
		err := r.bulkInsertErrs[0]
		r.bulkInsertErrs = r.bulkInsertErrs[1:]
		return nil, err
	}
	landed := make([]bool, len(records))
	for index, record := range records {
		landed[index] = true
		if r.applyUpdates && r.rows != nil {
			id, _ := record[constants.FieldID].(string)
			if id != "" {
				r.rows[strings.ToLower(table)+":"+id] = cloneSObject(record)
			}
		}
	}
	return landed, nil
}

func newPermissionResourceLockService(rows map[string]models.SObject) (*PersistenceService, *permissionResourceLockRepo) {
	return newPermissionResourceLockServiceWithPermissionSetAssignmentWriteValidation(rows, true)
}

func newPermissionResourceLockServiceWithoutPermissionSetAssignmentWriteValidation(rows map[string]models.SObject) (*PersistenceService, *permissionResourceLockRepo) {
	return newPermissionResourceLockServiceWithPermissionSetAssignmentWriteValidation(rows, false)
}

func newPermissionResourceLockServiceWithPermissionSetAssignmentWriteValidation(rows map[string]models.SObject, includePermissionSetAssignmentWriteValidation bool) (*PersistenceService, *permissionResourceLockRepo) {
	cascade := constants.DeleteRuleCascade
	permissionSetAssignmentSchema := &models.ObjectMetadata{
		APIName: constants.TablePermissionSetAssignment,
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
			{APIName: constants.FieldPermissionSetAssignment_AssigneeID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
			{APIName: constants.FieldPermissionSetAssignment_PermissionSetID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
			{APIName: constants.FieldIsDeleted, Type: constants.FieldTypeBoolean},
		},
	}
	if includePermissionSetAssignmentWriteValidation {
		permissionSetAssignmentSchema.AccessPolicy = permissionSetAssignmentWriteValidationAccessPolicy()
	}

	schemas := []*models.ObjectMetadata{
		{
			APIName:                 constants.TablePermissionSet,
			RequiredWritePermission: "ManageProfilesPermSets",
			AccessPolicy: &models.ObjectAccessPolicy{
				WriteValidation: &models.RecordWriteValidationAccessPolicy{
					Validators: append(
						systemProfileOwnedImmutableWriteValidationPolicies(),
						managedPermissionSetDefinitionWriteValidationPolicies()...,
					),
					OwnerResolution: &models.OwnerResolutionSpec{
						Paths: []models.OwnerResolutionPath{
							{
								Hops: []models.OwnerResolutionHop{
									{Self: true, Gate: &models.OwnerPredicate{Field: constants.FieldPermissionSet_IsOwnedByProfile, Is: true}},
									{ViaField: "ProfileId", ToObject: constants.TableProfile, SystemWhen: &models.OwnerPredicate{Field: constants.FieldProfile_IsSystem, Is: true}},
								},
							},
						},
					},
				},
			},
			Fields: []models.FieldMetadata{
				{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldPermissionSet_ProfileID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldPermissionSet_IsOwnedByProfile, Type: constants.FieldTypeBoolean, Createable: true, Updateable: true},
				{APIName: constants.FieldPermissionSet_OwnerPack, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: "Description", Type: constants.FieldTypeTextarea, Createable: true, Updateable: true},
			},
		},
		{
			APIName: constants.TableProfile,
			Fields: []models.FieldMetadata{
				{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldProfile_IsSystem, Type: constants.FieldTypeBoolean, Createable: true, Updateable: true},
				{APIName: constants.FieldName, Type: constants.FieldTypeString, Createable: true, Updateable: true},
			},
		},
		{
			APIName:                 constants.TableObjectPermissions,
			RequiredWritePermission: "ManageProfilesPermSets",
			AccessPolicy: &models.ObjectAccessPolicy{
				WriteValidation: &models.RecordWriteValidationAccessPolicy{
					Validators: append(
						append([]models.RecordWriteValidationPolicy{
							{Phase: string(contract.RecordWriteValidationBeforeCreate), Capability: securityWriteValidateObjectPermissionsCapability},
							{Phase: string(contract.RecordWriteValidationBeforeUpdate), Capability: securityWriteValidateObjectPermissionsCapability},
						}, systemProfileOwnedImmutableWriteValidationPolicies()...),
						managedPermissionSetDefinitionWriteValidationPolicies()...,
					),
					OwnerResolution: &models.OwnerResolutionSpec{
						Paths: []models.OwnerResolutionPath{
							{
								Hops: []models.OwnerResolutionHop{
									{ViaField: "ParentId", ToObject: constants.TablePermissionSet, Gate: &models.OwnerPredicate{Field: constants.FieldPermissionSet_IsOwnedByProfile, Is: true}},
									{ViaField: "ProfileId", ToObject: constants.TableProfile, SystemWhen: &models.OwnerPredicate{Field: constants.FieldProfile_IsSystem, Is: true}},
								},
							},
							{
								Hops: []models.OwnerResolutionHop{
									{ViaField: "ParentId", ToObject: constants.TableProfile, SystemWhen: &models.OwnerPredicate{Field: constants.FieldProfile_IsSystem, Is: true}},
								},
							},
						},
					},
				},
			},
			Fields: []models.FieldMetadata{
				{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldObjectPermissions_ParentID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldObjectPermissions_SobjectType, Type: constants.FieldTypeString, Createable: true, Updateable: true},
			},
		},
		{
			APIName:                 constants.TableFieldPermissions,
			RequiredWritePermission: "ManageProfilesPermSets",
			AccessPolicy: &models.ObjectAccessPolicy{
				WriteValidation: &models.RecordWriteValidationAccessPolicy{
					Validators: append(
						systemProfileOwnedImmutableWriteValidationPolicies(),
						managedPermissionSetDefinitionWriteValidationPolicies()...,
					),
					OwnerResolution: &models.OwnerResolutionSpec{
						Paths: []models.OwnerResolutionPath{
							{
								Hops: []models.OwnerResolutionHop{
									{ViaField: "ParentId", ToObject: constants.TablePermissionSet, Gate: &models.OwnerPredicate{Field: constants.FieldPermissionSet_IsOwnedByProfile, Is: true}},
									{ViaField: "ProfileId", ToObject: constants.TableProfile, SystemWhen: &models.OwnerPredicate{Field: constants.FieldProfile_IsSystem, Is: true}},
								},
							},
							{
								Hops: []models.OwnerResolutionHop{
									{ViaField: "ParentId", ToObject: constants.TableProfile, SystemWhen: &models.OwnerPredicate{Field: constants.FieldProfile_IsSystem, Is: true}},
								},
							},
						},
					},
				},
			},
			Fields: []models.FieldMetadata{
				{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldFieldPermissions_ParentID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: "Field", Type: constants.FieldTypeString, Createable: true, Updateable: true},
			},
		},
		{
			APIName: constants.TableProfileTabSetting,
			AccessPolicy: &models.ObjectAccessPolicy{
				WriteValidation: &models.RecordWriteValidationAccessPolicy{
					Validators:      systemProfileOwnedImmutableWriteValidationPolicies(),
					OwnerResolution: systemProfileOwnedImmutableOwnerResolutionSpec(),
				},
			},
			Fields: []models.FieldMetadata{
				{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldProfileTabSetting_ProfileID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldProfileTabSetting_TabName, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: "Visibility", Type: constants.FieldTypeString, Createable: true, Updateable: true},
			},
		},
		{
			APIName: constants.TablePermissionSetTabSetting,
			AccessPolicy: &models.ObjectAccessPolicy{
				WriteValidation: &models.RecordWriteValidationAccessPolicy{
					Validators: append(
						systemProfileOwnedImmutableWriteValidationPolicies(),
						managedPermissionSetDefinitionNegativeEdgeWriteValidationPolicies()...,
					),
					OwnerResolution: &models.OwnerResolutionSpec{
						Paths: []models.OwnerResolutionPath{{
							Hops: []models.OwnerResolutionHop{
								{
									ViaField: "PermissionSetId",
									ToObject: constants.TablePermissionSet,
									Gate:     &models.OwnerPredicate{Field: constants.FieldPermissionSet_IsOwnedByProfile, Is: true},
								},
								{
									ViaField:   "ProfileId",
									ToObject:   constants.TableProfile,
									SystemWhen: &models.OwnerPredicate{Field: constants.FieldProfile_IsSystem, Is: true},
								},
							},
						}},
					},
				},
			},
			Fields: []models.FieldMetadata{
				{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldPermissionSetTabSetting_PermissionSetID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldPermissionSetTabSetting_TabName, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: "Visibility", Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldIsDeleted, Type: constants.FieldTypeBoolean, Createable: false, Updateable: false},
			},
		},
		{
			APIName: constants.TableConnectedApp,
			Fields: []models.FieldMetadata{
				{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
			},
		},
		{
			APIName: constants.TableConnectedAppAccess,
			AccessPolicy: &models.ObjectAccessPolicy{
				WriteValidation: &models.RecordWriteValidationAccessPolicy{
					Validators: append(
						systemProfileOwnedImmutableWriteValidationPolicies(),
						managedPermissionSetDefinitionNegativeEdgeWriteValidationPolicies()...,
					),
					OwnerResolution: &models.OwnerResolutionSpec{
						Paths: []models.OwnerResolutionPath{
							{
								Hops: []models.OwnerResolutionHop{
									{ViaField: "ProfileId", ToObject: constants.TableProfile, SystemWhen: &models.OwnerPredicate{Field: constants.FieldProfile_IsSystem, Is: true}},
								},
							},
							{
								Hops: []models.OwnerResolutionHop{
									{ViaField: "PermissionSetId", ToObject: constants.TablePermissionSet, Gate: &models.OwnerPredicate{Field: constants.FieldPermissionSet_IsOwnedByProfile, Is: true}},
									{ViaField: "ProfileId", ToObject: constants.TableProfile, SystemWhen: &models.OwnerPredicate{Field: constants.FieldProfile_IsSystem, Is: true}},
								},
							},
						},
					},
				},
			},
			Fields: []models.FieldMetadata{
				{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{
					APIName:     constants.FieldSysConnectedAppAccess_ConnectedAppID,
					Type:        constants.FieldTypeReference,
					ReferenceTo: []string{constants.TableConnectedApp},
					DeleteRule:  &cascade,
					Createable:  true,
					Updateable:  true,
				},
				{APIName: constants.FieldSysConnectedAppAccess_ProfileID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
				{APIName: constants.FieldSysConnectedAppAccess_PermissionSetID, Type: constants.FieldTypeString, Createable: true, Updateable: true},
			},
		},
		permissionSetAssignmentSchema,
	}

	metaSvc := metadataFeature.NewMetadataService(&stubUpsertMetaRepo{schemas: schemas}, nil, nil)
	permSvc := permissions.NewPermissionService(
		&stubUpsertPermRepo{
			grantCreate: true,
			grantEdit:   true,
			systemPermsByProfile: map[string]map[string]bool{
				"permission_resource_admin": {
					string(constants.SuperUserCapability):        true,
					string(constants.PermManageProfilesPermSets): true,
				},
			},
		},
		metaSvc,
		nil,
	)
	repo := &permissionResourceLockRepo{rows: rows}

	ps := &PersistenceService{
		repo:                      repo,
		rollup:                    noopRollupProcessor{},
		metadata:                  metaSvc,
		permissions:               permSvc,
		eventBus:                  &eventFeature.NoOpEventBus{},
		validator:                 metadataFeature.NewValidationService(formula.NewEngine()),
		schemaValidator:           validation.NewSchemaValidator(),
		txManager:                 inlineTxManager{},
		parentLaneAdmission:       &fakeParentLaneAdmissionController{},
		assignmentPolicyAuthority: &activeClaimAuthorityTestGuard{},
	}
	ps.registerBuiltInSecurityWriteValidators()
	return installNoOpPermissionStateAuthority(ps), repo
}

func permissionSetAssignmentWriteValidationAccessPolicy() *models.ObjectAccessPolicy {
	return &models.ObjectAccessPolicy{
		WriteValidation: &models.RecordWriteValidationAccessPolicy{
			Validators: []models.RecordWriteValidationPolicy{
				{Phase: string(recordWriteValidationBeforeCreate), Capability: securityWriteValidatePermissionSetAssignmentTargetCapability},
				{Phase: string(recordWriteValidationBeforeUpdate), Capability: securityWriteValidatePermissionSetAssignmentTargetCapability},
				{Phase: string(recordWriteValidationBeforeDelete), Capability: securityWriteValidatePermissionSetAssignmentTargetCapability},
				{Phase: string(recordWriteValidationBeforeRestore), Capability: securityWriteValidatePermissionSetAssignmentTargetCapability},
				{Phase: string(recordWriteValidationBeforePurge), Capability: securityWriteValidatePermissionSetAssignmentTargetCapability},
			},
		},
	}
}

func permissionResourceLockRows() map[string]models.SObject {
	return map[string]models.SObject{
		strings.ToLower(constants.TablePermissionSet) + ":ps-system": {
			constants.FieldID:                             "ps-system",
			constants.FieldPermissionSet_ProfileID:        "profile-system",
			constants.FieldPermissionSet_IsOwnedByProfile: true,
		},
		strings.ToLower(constants.TablePermissionSet) + ":ps-custom": {
			constants.FieldID:                             "ps-custom",
			constants.FieldPermissionSet_ProfileID:        "profile-custom",
			constants.FieldPermissionSet_IsOwnedByProfile: true,
		},
		strings.ToLower(constants.TablePermissionSet) + ":ps-managed": {
			constants.FieldID: "ps-managed",
			constants.FieldPermissionSet_IsOwnedByProfile: false,
			constants.FieldPermissionSet_OwnerPack:        "official:security",
			constants.FieldPermissionSet_IsActive:         true,
			constants.FieldIsDeleted:                      false,
			"Description":                                 "managed",
		},
		strings.ToLower(constants.TablePermissionSet) + ":ps-retired": {
			constants.FieldID: "ps-retired",
			constants.FieldPermissionSet_IsOwnedByProfile: false,
			constants.FieldPermissionSet_OwnerPack:        "official:security",
			constants.FieldPermissionSet_IsActive:         false,
			constants.FieldIsDeleted:                      true,
		},
		strings.ToLower(constants.TablePermissionSet) + ":ps-managed-next": {
			constants.FieldID: "ps-managed-next",
			constants.FieldPermissionSet_IsOwnedByProfile: false,
			constants.FieldPermissionSet_OwnerPack:        "official:security",
			constants.FieldPermissionSet_IsActive:         true,
			constants.FieldIsDeleted:                      false,
		},
		strings.ToLower(constants.TablePermissionSet) + ":ps-system-storage": {
			constants.FieldID:                             "ps-system-storage",
			constants.FieldPermissionSet_ProfileID:        "profile-system",
			constants.FieldPermissionSet_IsOwnedByProfile: 1, // storage-shaped truthy value
		},
		strings.ToLower(constants.TableProfile) + ":profile-system": {
			constants.FieldID:               "profile-system",
			constants.FieldProfile_IsSystem: true,
			constants.FieldName:             "System Administrator",
		},
		strings.ToLower(constants.TableProfile) + ":profile-custom": {
			constants.FieldID:               "profile-custom",
			constants.FieldProfile_IsSystem: false,
			constants.FieldName:             "Custom Profile",
		},
		strings.ToLower(constants.TableObjectPermissions) + ":op-system": {
			constants.FieldID:                         "op-system",
			constants.FieldObjectPermissions_ParentID: "ps-system",
		},
		strings.ToLower(constants.TableObjectPermissions) + ":op-managed": {
			constants.FieldID:                            "op-managed",
			constants.FieldObjectPermissions_ParentID:    "ps-managed",
			constants.FieldObjectPermissions_SobjectType: constants.TablePermissionSetTabSetting,
		},
		strings.ToLower(constants.TableFieldPermissions) + ":fp-system": {
			constants.FieldID:                        "fp-system",
			constants.FieldFieldPermissions_ParentID: "ps-system",
			"Field":                                  "Account.Name",
		},
		strings.ToLower(constants.TableProfileTabSetting) + ":pts-system": {
			constants.FieldID:                          "pts-system",
			constants.FieldProfileTabSetting_ProfileID: "profile-system",
			constants.FieldProfileTabSetting_TabName:   "Account",
			"Visibility":                               "DefaultOn",
		},
		strings.ToLower(constants.TableProfileTabSetting) + ":pts-custom": {
			constants.FieldID:                          "pts-custom",
			constants.FieldProfileTabSetting_ProfileID: "profile-custom",
			constants.FieldProfileTabSetting_TabName:   "Account",
			"Visibility":                               "DefaultOn",
		},
		strings.ToLower(constants.TablePermissionSetTabSetting) + ":psts-system": {
			constants.FieldID: "psts-system",
			constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-system",
			constants.FieldPermissionSetTabSetting_TabName:         "Account",
			"Visibility": "Available",
		},
		strings.ToLower(constants.TablePermissionSetTabSetting) + ":psts-custom": {
			constants.FieldID: "psts-custom",
			constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-custom",
			constants.FieldPermissionSetTabSetting_TabName:         "Account",
			"Visibility": "Available",
		},
		strings.ToLower(constants.TablePermissionSetTabSetting) + ":psts-managed": {
			constants.FieldID: "psts-managed",
			constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-managed",
			constants.FieldPermissionSetTabSetting_TabName:         "Account",
			"Visibility": "Available",
		},
		strings.ToLower(constants.TableConnectedAppAccess) + ":caa-system-malformed": {
			constants.FieldID: "caa-system-malformed",
			constants.FieldSysConnectedAppAccess_ConnectedAppID:  "app-1",
			constants.FieldSysConnectedAppAccess_ProfileID:       "profile-custom",
			constants.FieldSysConnectedAppAccess_PermissionSetID: "ps-system",
		},
		strings.ToLower(constants.TableConnectedApp) + ":app-1": {
			constants.FieldID: "app-1",
		},
		strings.ToLower(constants.TablePermissionSetAssignment) + ":psa-system": {
			constants.FieldID:                                      "psa-system",
			constants.FieldIsDeleted:                               false,
			constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-system",
			constants.FieldPermissionSetAssignment_PermissionSetID: "ps-custom",
		},
		strings.ToLower(constants.TablePermissionSetAssignment) + ":psa-managed": {
			constants.FieldID:                                      "psa-managed",
			constants.FieldIsDeleted:                               false,
			constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-managed",
			constants.FieldPermissionSetAssignment_PermissionSetID: "ps-managed",
		},
		strings.ToLower(constants.TablePermissionSetAssignment) + ":psa-retired": {
			constants.FieldID:                                      "psa-retired",
			constants.FieldIsDeleted:                               false,
			constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-retired",
			constants.FieldPermissionSetAssignment_PermissionSetID: "ps-retired",
		},
	}
}

func permissionAdminUser() *models.UserSession {
	return &models.UserSession{
		ID:        "admin",
		ProfileID: "permission_resource_admin",
	}
}

func assertPermissionResourceLocked(t *testing.T, err error) {
	t.Helper()
	if err == nil {
		t.Fatal("expected forbidden error")
	}
	if status := apperrors.GetHTTPStatus(err); status != http.StatusForbidden {
		t.Fatalf("expected 403, got %d: %v", status, err)
	}
	if !strings.Contains(err.Error(), systemProfileOwnedPermissionResourceLockedMessage) {
		t.Fatalf("expected locked message, got %v", err)
	}
}

func assertManagedPermissionSetDefinitionLocked(t *testing.T, err error) {
	t.Helper()
	if err == nil {
		t.Fatal("expected forbidden error")
	}
	if status := apperrors.GetHTTPStatus(err); status != http.StatusForbidden {
		t.Fatalf("expected 403, got %d: %v", status, err)
	}
	if !strings.Contains(err.Error(), packManagedPermissionSetDefinitionLockedMessage) {
		t.Fatalf("expected managed definition message, got %v", err)
	}
}

func TestPersistenceUpdateRejectsManagedPermissionSetRoot(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	err := ps.Update(context.Background(), constants.TablePermissionSet, "ps-managed", models.SObject{
		"Description": "ordinary mutation",
	}, permissionAdminUser())
	assertManagedPermissionSetDefinitionLocked(t, err)
	if repo.updateCalls != 0 {
		t.Fatalf("expected no repo update, got %d", repo.updateCalls)
	}
}

func TestPersistenceInsertRejectsManagedPermissionSetModeledGrant(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	_, err := ps.Insert(context.Background(), constants.TableObjectPermissions, models.SObject{
		constants.FieldObjectPermissions_ParentID:    "ps-managed",
		constants.FieldObjectPermissions_SobjectType: constants.TablePermissionSetTabSetting,
	}, permissionAdminUser())
	assertManagedPermissionSetDefinitionLocked(t, err)
	if repo.insertCalls != 0 {
		t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
	}
}

func TestPersistenceBulkInsertRejectsManagedPermissionSetModeledGrant(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	result, err := ps.BulkInsert(context.Background(), constants.TableObjectPermissions, []models.SObject{{
		constants.FieldObjectPermissions_ParentID:    "ps-managed",
		constants.FieldObjectPermissions_SobjectType: constants.TablePermissionSetTabSetting,
	}}, permissionAdminUser(), BulkInsertOptions{})
	if err == nil || result.FailedCount != 1 {
		t.Fatalf("BulkInsert() result = %+v, error = %v, want managed definition rejection", result, err)
	}
	if len(result.Errors) != 1 || !strings.Contains(result.Errors[0], packManagedPermissionSetDefinitionLockedMessage) {
		t.Fatalf("BulkInsert() errors = %#v, want managed definition message", result.Errors)
	}
	if repo.bulkInsertCalls != 0 {
		t.Fatalf("expected no repo bulk insert, got %d", repo.bulkInsertCalls)
	}
}

func TestPersistenceBulkInsertPreservesUnmanagedRowWhenSameBatchContainsManagedGrant(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	result, err := ps.BulkInsert(context.Background(), constants.TableObjectPermissions, []models.SObject{
		{
			constants.FieldObjectPermissions_ParentID:    "ps-managed",
			constants.FieldObjectPermissions_SobjectType: constants.TablePermissionSetTabSetting,
		},
		{
			constants.FieldObjectPermissions_ParentID:    "ps-custom",
			constants.FieldObjectPermissions_SobjectType: constants.TablePermissionSetTabSetting,
		},
	}, permissionAdminUser(), BulkInsertOptions{})
	if err != nil {
		t.Fatalf("BulkInsert() returned batch error instead of partial success: %v", err)
	}
	if result.SuccessCount != 1 || result.FailedCount != 1 {
		t.Fatalf("BulkInsert() result = %+v, want one success and one managed-row failure", result)
	}
	if len(result.Errors) != 1 || !strings.Contains(result.Errors[0], packManagedPermissionSetDefinitionLockedMessage) {
		t.Fatalf("BulkInsert() errors = %#v, want one managed definition rejection", result.Errors)
	}
	if repo.bulkInsertCalls != 1 {
		t.Fatalf("repo bulk insert calls = %d, want one call for the unmanaged survivor", repo.bulkInsertCalls)
	}
}

func TestPersistenceBulkInsertForcedPrelockRetryLocksAllFinalImageParentsInGlobalOrder(t *testing.T) {
	rows := permissionResourceLockRows()
	rows["account:account-1"] = models.SObject{constants.FieldID: "account-1"}
	ps, repo := newPermissionResourceLockService(rows)
	txManager := &recordingBulkTxManager{}
	ps.txManager = txManager
	repo.bulkInsertErrs = []error{
		&parentLockPrelockRetryError{
			keys: []recordlocks.ParentKey{{
				ObjectName: "Account",
				RecordID:   "account-1",
			}},
			reason: "late parent",
		},
	}

	result, err := ps.BulkInsert(context.Background(), constants.TableConnectedAppAccess, []models.SObject{{
		constants.FieldSysConnectedAppAccess_ConnectedAppID:  "app-1",
		constants.FieldSysConnectedAppAccess_PermissionSetID: "ps-custom",
	}}, permissionAdminUser(), BulkInsertOptions{})
	if err != nil {
		t.Fatalf("BulkInsert(): %v", err)
	}
	if result.SuccessCount != 1 || result.FailedCount != 0 {
		t.Fatalf("BulkInsert() result = %+v, want one success", result)
	}
	wantRetryOrder := []string{
		constants.TableConnectedApp + ":app-1",
		"Account:account-1",
		constants.TablePermissionSet + ":ps-custom",
	}
	if len(repo.lockCalls) < len(wantRetryOrder) {
		t.Fatalf("lock calls = %#v, want global-order retry suffix %#v", repo.lockCalls, wantRetryOrder)
	}
	retryStart := len(repo.lockCalls) - len(wantRetryOrder)
	for index := range wantRetryOrder {
		if repo.lockCalls[retryStart+index] != wantRetryOrder[index] {
			t.Fatalf("lock calls = %#v, want global-order retry suffix %#v", repo.lockCalls, wantRetryOrder)
		}
	}
	if repo.bulkInsertCalls != 2 {
		t.Fatalf("repo bulk insert calls = %d, want 2", repo.bulkInsertCalls)
	}
	if txManager.pessimisticCalls != 2 {
		t.Fatalf("pessimistic calls = %d, want 2", txManager.pessimisticCalls)
	}
}

func TestPersistenceManagedModeledGrantLocksParentBeforeChild(t *testing.T) {
	for _, tc := range []struct {
		name   string
		mutate func(*PersistenceService) error
	}{
		{
			name: "update",
			mutate: func(ps *PersistenceService) error {
				return ps.Update(context.Background(), constants.TableObjectPermissions, "op-managed", models.SObject{
					constants.FieldObjectPermissions_SobjectType: constants.TableFieldPermissions,
				}, permissionAdminUser())
			},
		},
		{
			name: "delete",
			mutate: func(ps *PersistenceService) error {
				return ps.Delete(context.Background(), constants.TableObjectPermissions, "op-managed", permissionAdminUser())
			},
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
			assertManagedPermissionSetDefinitionLocked(t, tc.mutate(ps))
			want := []string{
				constants.TablePermissionSet + ":ps-managed",
				constants.TableObjectPermissions + ":op-managed",
			}
			if len(repo.lockCalls) < len(want) {
				t.Fatalf("lock calls = %#v, want parent-before-child prefix %#v", repo.lockCalls, want)
			}
			for i := range want {
				if repo.lockCalls[i] != want[i] {
					t.Fatalf("lock calls = %#v, want parent-before-child prefix %#v", repo.lockCalls, want)
				}
			}
		})
	}
}

func TestPersistenceNegativeEdgeRejectsAddButAllowsDetachAndDelete(t *testing.T) {
	t.Run("add", func(t *testing.T) {
		ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
		_, err := ps.Insert(context.Background(), constants.TablePermissionSetTabSetting, models.SObject{
			constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-managed",
			constants.FieldPermissionSetTabSetting_TabName:         "Account",
			"Visibility": "Available",
		}, permissionAdminUser())
		assertManagedPermissionSetDefinitionLocked(t, err)
		if repo.insertCalls != 0 {
			t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
		}
	})

	t.Run("detach", func(t *testing.T) {
		ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
		err := ps.Update(context.Background(), constants.TablePermissionSetTabSetting, "psts-managed", models.SObject{
			constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-custom",
		}, permissionAdminUser())
		if err != nil {
			t.Fatalf("negative-edge detach rejected: %v", err)
		}
		if repo.updateCalls != 1 {
			t.Fatalf("repo update calls = %d, want 1", repo.updateCalls)
		}
		want := []string{
			constants.TablePermissionSet + ":ps-custom",
			constants.TablePermissionSet + ":ps-managed",
			constants.TablePermissionSetTabSetting + ":psts-managed",
		}
		if len(repo.lockCalls) < len(want) {
			t.Fatalf("lock calls = %#v, want old and final parents before child %#v", repo.lockCalls, want)
		}
		for i := range want {
			if repo.lockCalls[i] != want[i] {
				t.Fatalf("lock calls = %#v, want old and final parents before child %#v", repo.lockCalls, want)
			}
		}
	})

	t.Run("delete", func(t *testing.T) {
		ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
		if err := ps.Delete(context.Background(), constants.TablePermissionSetTabSetting, "psts-managed", permissionAdminUser()); err != nil {
			t.Fatalf("negative-edge delete rejected: %v", err)
		}
		if repo.updateCalls != 1 {
			t.Fatalf("repo update calls = %d, want soft-delete update", repo.updateCalls)
		}
	})
}

func TestPersistenceBeforeSaveManagedRetargetRetriesWithParentBeforeChild(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeUpdate {
			record[constants.FieldPermissionSetTabSetting_PermissionSetID] = "ps-managed"
		}
	}}

	err := ps.Update(context.Background(), constants.TablePermissionSetTabSetting, "psts-custom", models.SObject{
		"Visibility": "Hidden",
	}, permissionAdminUser())
	assertManagedPermissionSetDefinitionLocked(t, err)
	if repo.updateCalls != 0 {
		t.Fatalf("repo update calls = %d, want none", repo.updateCalls)
	}
	want := []string{
		constants.TablePermissionSet + ":ps-custom",
		constants.TablePermissionSetTabSetting + ":psts-custom",
		constants.TablePermissionSet + ":ps-custom",
		constants.TablePermissionSet + ":ps-managed",
		constants.TablePermissionSetTabSetting + ":psts-custom",
	}
	if len(repo.lockCalls) != len(want) {
		t.Fatalf("lock calls = %#v, want retry lock sequence %#v", repo.lockCalls, want)
	}
	for i := range want {
		if repo.lockCalls[i] != want[i] {
			t.Fatalf("lock calls = %#v, want retry lock sequence %#v", repo.lockCalls, want)
		}
	}
}

func TestPersistenceInsertRejectsSystemProfileOwnedPermissionSetTabSetting(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	_, err := ps.Insert(context.Background(), constants.TablePermissionSetTabSetting, models.SObject{
		constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-system",
		constants.FieldPermissionSetTabSetting_TabName:         "Account",
		"Visibility": "Available",
	}, permissionAdminUser())
	assertPermissionResourceLocked(t, err)
	if repo.insertCalls != 0 {
		t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
	}
}

func TestPersistenceInsertRejectsSystemProfileOwnedProfileTabSetting(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	_, err := ps.Insert(context.Background(), constants.TableProfileTabSetting, models.SObject{
		constants.FieldProfileTabSetting_ProfileID: "profile-system",
		constants.FieldProfileTabSetting_TabName:   "Account",
		"Visibility":                               "DefaultOn",
	}, permissionAdminUser())
	assertPermissionResourceLocked(t, err)
	if repo.insertCalls != 0 {
		t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
	}
}

func TestPersistenceUpdateRejectsSystemProfileOwnedPermissionSetTabSettingWithoutOwnerPayload(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	err := ps.Update(context.Background(), constants.TablePermissionSetTabSetting, "psts-system", models.SObject{
		"Visibility": "Hidden",
	}, permissionAdminUser())
	assertPermissionResourceLocked(t, err)
	if repo.updateCalls != 0 {
		t.Fatalf("expected no repo update, got %d", repo.updateCalls)
	}
}

func TestPersistenceInsertPermissionSetAssignmentFailsClosedWhenEffectiveMetadataDropsWriteValidator(t *testing.T) {
	ps, repo := newPermissionResourceLockServiceWithoutPermissionSetAssignmentWriteValidation(permissionResourceLockRows())

	_, err := ps.Insert(context.Background(), constants.TablePermissionSetAssignment, models.SObject{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-system",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-system",
	}, permissionAdminUser())
	if err == nil {
		t.Fatal("expected insert to fail")
	}
	if !strings.Contains(err.Error(), "missing record write validation metadata") ||
		!strings.Contains(err.Error(), constants.TablePermissionSetAssignment) {
		t.Fatalf("expected missing write-validation metadata error, got %v", err)
	}
	if repo.insertCalls != 0 {
		t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
	}
}

func TestPersistenceInsertPermissionSetAssignmentLocksTargetPermissionSet(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	if _, err := ps.Insert(context.Background(), constants.TablePermissionSetAssignment, models.SObject{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-managed",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-managed",
	}, permissionAdminUser()); err != nil {
		t.Fatalf("Insert PermissionSetAssignment: %v", err)
	}
	if repo.insertCalls != 1 {
		t.Fatalf("repo insert calls = %d, want 1", repo.insertCalls)
	}
	if len(repo.lockCalls) != 1 || repo.lockCalls[0] != constants.TablePermissionSet+":ps-managed" {
		t.Fatalf("root lock calls = %#v, want PermissionSet:ps-managed", repo.lockCalls)
	}
}

func TestPersistenceInsertPermissionSetAssignmentRejectsRetiredTarget(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	_, err := ps.Insert(context.Background(), constants.TablePermissionSetAssignment, models.SObject{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-retired",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-retired",
	}, permissionAdminUser())
	if err == nil || !strings.Contains(err.Error(), "inactive or deleted") {
		t.Fatalf("Insert PermissionSetAssignment error = %v, want retired-target rejection", err)
	}
	if repo.insertCalls != 0 {
		t.Fatalf("repo insert calls = %d, want 0", repo.insertCalls)
	}
	if len(repo.lockCalls) != 1 || repo.lockCalls[0] != constants.TablePermissionSet+":ps-retired" {
		t.Fatalf("root lock calls = %#v, want PermissionSet:ps-retired", repo.lockCalls)
	}
}

func TestPersistenceInsertPermissionSetAssignmentLocksBeforeSaveTarget(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeCreate {
			record[constants.FieldPermissionSetAssignment_PermissionSetID] = "ps-managed-next"
		}
	}}

	if _, err := ps.Insert(context.Background(), constants.TablePermissionSetAssignment, models.SObject{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-managed",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-managed",
	}, permissionAdminUser()); err != nil {
		t.Fatalf("Insert PermissionSetAssignment: %v", err)
	}
	if repo.insertCalls != 1 {
		t.Fatalf("repo insert calls = %d, want 1", repo.insertCalls)
	}
	var lockedFinalTarget bool
	for _, call := range repo.lockCalls {
		if call == constants.TablePermissionSet+":ps-managed-next" {
			lockedFinalTarget = true
			break
		}
	}
	if !lockedFinalTarget {
		t.Fatalf("lock calls = %#v, want final PermissionSet target lock", repo.lockCalls)
	}
}

func TestPersistenceUpsertCreatePermissionSetAssignmentLocksTargetPermissionSet(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	_, created, err := ps.UpsertByExternalID(
		context.Background(),
		constants.TablePermissionSetAssignment,
		constants.FieldPermissionSetAssignment_AssigneeID,
		"assignee-upsert",
		models.SObject{
			constants.FieldPermissionSetAssignment_PermissionSetID: "ps-managed",
		},
		permissionAdminUser(),
	)
	if err != nil {
		t.Fatalf("UpsertByExternalID PermissionSetAssignment: %v", err)
	}
	if !created {
		t.Fatal("UpsertByExternalID created = false, want create path")
	}
	if repo.insertCalls != 1 {
		t.Fatalf("repo insert calls = %d, want 1", repo.insertCalls)
	}
	if len(repo.lockCalls) != 1 || repo.lockCalls[0] != constants.TablePermissionSet+":ps-managed" {
		t.Fatalf("lock calls = %#v, want PermissionSet:ps-managed", repo.lockCalls)
	}
}

func TestPersistenceUpdatePermissionSetAssignmentLocksOldAndNewTargets(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	if err := ps.Update(context.Background(), constants.TablePermissionSetAssignment, "psa-managed", models.SObject{
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-managed-next",
	}, permissionAdminUser()); err != nil {
		t.Fatalf("Update PermissionSetAssignment: %v", err)
	}
	if repo.updateCalls != 1 {
		t.Fatalf("repo update calls = %d, want 1", repo.updateCalls)
	}
	want := []string{
		constants.TablePermissionSet + ":ps-managed",
		constants.TablePermissionSet + ":ps-managed-next",
		constants.TablePermissionSetAssignment + ":psa-managed",
	}
	if len(repo.lockCalls) != len(want) ||
		repo.lockCalls[0] != want[0] ||
		repo.lockCalls[1] != want[1] ||
		repo.lockCalls[2] != want[2] {
		t.Fatalf("lock calls = %#v, want %#v", repo.lockCalls, want)
	}
}

func TestPersistenceUpdatePermissionSetAssignmentLocksBeforeSaveTarget(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeUpdate {
			record[constants.FieldPermissionSetAssignment_PermissionSetID] = "ps-managed-next"
		}
	}}

	if err := ps.Update(context.Background(), constants.TablePermissionSetAssignment, "psa-managed", models.SObject{
		constants.FieldPermissionSetAssignment_AssigneeID: "assignee-managed-next",
	}, permissionAdminUser()); err != nil {
		t.Fatalf("Update PermissionSetAssignment: %v", err)
	}
	if repo.updateCalls != 1 {
		t.Fatalf("repo update calls = %d, want 1", repo.updateCalls)
	}
	var lockedFinalTarget bool
	for _, call := range repo.lockCalls {
		if call == constants.TablePermissionSet+":ps-managed-next" {
			lockedFinalTarget = true
			break
		}
	}
	if !lockedFinalTarget {
		t.Fatalf("lock calls = %#v, want before-save PermissionSet target lock", repo.lockCalls)
	}
}

func TestPersistenceUpdateRecordsAllOrNoneRetriesBeforeSavePermissionSetTarget(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	txManager := &recordingBulkTxManager{}
	ps.txManager = txManager
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeUpdate {
			record[constants.FieldPermissionSetAssignment_PermissionSetID] = "ps-managed-next"
		}
	}}

	results, err := ps.UpdateRecords(context.Background(), []models.SObject{{
		"attributes":      map[string]interface{}{"type": constants.TablePermissionSetAssignment},
		constants.FieldID: "psa-managed",
		constants.FieldPermissionSetAssignment_AssigneeID: "assignee-managed-next",
	}}, true, permissionAdminUser())
	if err != nil {
		t.Fatalf("UpdateRecords PermissionSetAssignment: %v", err)
	}
	if len(results) != 1 || !results[0].Success {
		t.Fatalf("UpdateRecords results = %#v, want one success", results)
	}
	if repo.updateCalls != 1 {
		t.Fatalf("repo update calls = %d, want 1", repo.updateCalls)
	}
	if txManager.pessimisticCalls != 2 {
		t.Fatalf("pessimistic transaction calls = %d, want initial attempt plus forced-prelock retry", txManager.pessimisticCalls)
	}
	if len(repo.lockCalls) == 0 || repo.lockCalls[len(repo.lockCalls)-2] != constants.TablePermissionSet+":ps-managed-next" {
		t.Fatalf("lock calls = %#v, want final retry to prelock before-save PermissionSet target", repo.lockCalls)
	}
}

func TestPreviewAndCommitPreparedUpdatePermissionSetAssignmentDefersLockAndRevalidates(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	prepared, err := ps.PreviewBeforeUpdate(
		context.Background(),
		constants.TablePermissionSetAssignment,
		"psa-managed",
		models.SObject{
			constants.FieldPermissionSetAssignment_PermissionSetID: "ps-managed-next",
		},
		nil,
		permissionAdminUser(),
	)
	if err != nil {
		t.Fatalf("PreviewBeforeUpdate PermissionSetAssignment: %v", err)
	}
	if prepared == nil ||
		prepared.Record[constants.FieldPermissionSetAssignment_PermissionSetID] != "ps-managed-next" {
		t.Fatalf("prepared update = %#v, want ps-managed-next target", prepared)
	}
	if len(repo.lockCalls) != 1 ||
		repo.lockCalls[0] != constants.TablePermissionSetAssignment+":psa-managed" ||
		repo.updateCalls != 0 {
		t.Fatalf("preview locks or writes = %#v / %d, want only existing child-row lock and no write", repo.lockCalls, repo.updateCalls)
	}

	repo.rows[strings.ToLower(constants.TablePermissionSet)+":ps-managed-next"][constants.FieldPermissionSet_IsActive] = false
	repo.rows[strings.ToLower(constants.TablePermissionSet)+":ps-managed-next"][constants.FieldIsDeleted] = true
	repo.lockCalls = nil

	err = ps.CommitPreparedUpdate(
		context.Background(),
		constants.TablePermissionSetAssignment,
		"psa-managed",
		prepared,
		nil,
		permissionAdminUser(),
	)
	if err == nil || !strings.Contains(err.Error(), inactivePermissionSetAssignmentMessage) {
		t.Fatalf("CommitPreparedUpdate error = %v, want stale preview target rejection", err)
	}
	if repo.updateCalls != 0 {
		t.Fatalf("repo update calls = %d, want 0 after final target rejection", repo.updateCalls)
	}
	var lockedFinalTarget bool
	for _, call := range repo.lockCalls {
		if call == constants.TablePermissionSet+":ps-managed-next" {
			lockedFinalTarget = true
			break
		}
	}
	if !lockedFinalTarget {
		t.Fatalf("commit lock calls = %#v, want final PermissionSet target lock", repo.lockCalls)
	}
}

func TestPersistenceUpdateRejectsProfileOwnedPermissionSetAssignment(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	err := ps.Update(context.Background(), constants.TablePermissionSetAssignment, "psa-system", models.SObject{
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-system",
	}, permissionAdminUser())
	if err == nil {
		t.Fatal("expected update to fail")
	}
	if !strings.Contains(err.Error(), profileOwnedPermissionSetAssignmentMessage) {
		t.Fatalf("expected profile-owned assignment message, got %v", err)
	}
	if repo.updateCalls != 0 {
		t.Fatalf("expected no repo update, got %d", repo.updateCalls)
	}
}

func TestPersistenceDeleteRejectsSystemProfileOwnedPermissionSetTabSetting(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	err := ps.Delete(context.Background(), constants.TablePermissionSetTabSetting, "psts-system", permissionAdminUser())
	assertPermissionResourceLocked(t, err)
	if repo.deleteCalls != 0 {
		t.Fatalf("expected no repo delete, got %d", repo.deleteCalls)
	}
}

func TestPersistenceDeleteRejectsProfileOwnedPermissionSetAssignment(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	err := ps.Delete(context.Background(), constants.TablePermissionSetAssignment, "psa-system", permissionAdminUser())
	if err == nil {
		t.Fatal("expected delete to fail")
	}
	if !strings.Contains(err.Error(), profileOwnedPermissionSetAssignmentMessage) {
		t.Fatalf("expected profile-owned assignment message, got %v", err)
	}
	if repo.updateCalls != 0 {
		t.Fatalf("expected no repo update, got %d", repo.updateCalls)
	}
}

func TestPersistenceDeletePermissionSetAssignmentLocksTargetBeforeAssignment(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	if err := ps.Delete(
		context.Background(),
		constants.TablePermissionSetAssignment,
		"psa-managed",
		permissionAdminUser(),
	); err != nil {
		t.Fatalf("Delete PermissionSetAssignment: %v", err)
	}
	if repo.updateCalls != 1 {
		t.Fatalf("repo update calls = %d, want 1 soft delete", repo.updateCalls)
	}
	if repo.insertCalls != 1 {
		t.Fatalf("repo insert calls = %d, want 1 recycle-bin tombstone", repo.insertCalls)
	}
	want := []string{
		constants.TablePermissionSet + ":ps-managed",
		constants.TablePermissionSetAssignment + ":psa-managed",
	}
	if len(repo.lockCalls) != len(want) ||
		repo.lockCalls[0] != want[0] ||
		repo.lockCalls[1] != want[1] {
		t.Fatalf("lock calls = %#v, want %#v", repo.lockCalls, want)
	}
}

func TestPersistenceDeletePermissionSetAssignmentAllowsRetiredTarget(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	if err := ps.Delete(
		context.Background(),
		constants.TablePermissionSetAssignment,
		"psa-retired",
		permissionAdminUser(),
	); err != nil {
		t.Fatalf("Delete PermissionSetAssignment with retired target: %v", err)
	}
	if repo.updateCalls != 1 {
		t.Fatalf("repo update calls = %d, want 1 soft delete", repo.updateCalls)
	}
	want := []string{
		constants.TablePermissionSet + ":ps-retired",
		constants.TablePermissionSetAssignment + ":psa-retired",
	}
	if len(repo.lockCalls) != len(want) ||
		repo.lockCalls[0] != want[0] ||
		repo.lockCalls[1] != want[1] {
		t.Fatalf("lock calls = %#v, want %#v", repo.lockCalls, want)
	}
}

func TestPersistenceInsertRejectsBeforeSaveFlowRetargetIntoSystemProfileOwnedPermissionSetTabSetting(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeCreate {
			record[constants.FieldPermissionSetTabSetting_PermissionSetID] = "ps-system"
		}
	}}

	_, err := ps.Insert(context.Background(), constants.TablePermissionSetTabSetting, models.SObject{
		constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-custom",
		constants.FieldPermissionSetTabSetting_TabName:         "Account",
		"Visibility": "Available",
	}, permissionAdminUser())
	assertPermissionResourceLocked(t, err)
	if repo.insertCalls != 0 {
		t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
	}
}

func TestPersistenceInsertRejectsBeforeSaveFlowRetargetIntoSystemProfileOwnedPermissionSetAssignment(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeCreate {
			record[constants.FieldPermissionSetAssignment_PermissionSetID] = "ps-system"
		}
	}}

	_, err := ps.Insert(context.Background(), constants.TablePermissionSetAssignment, models.SObject{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-system",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-custom",
	}, permissionAdminUser())
	if err == nil {
		t.Fatal("expected insert to fail")
	}
	if !strings.Contains(err.Error(), profileOwnedPermissionSetAssignmentMessage) {
		t.Fatalf("expected profile-owned assignment message, got %v", err)
	}
	if repo.insertCalls != 0 {
		t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
	}
}

func TestPersistenceUpsertByExternalIDRejectsBeforeSaveFlowRetargetIntoSystemProfileOwnedPermissionSetAssignment(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeCreate {
			record[constants.FieldPermissionSetAssignment_PermissionSetID] = "ps-system"
		}
	}}

	_, _, err := ps.UpsertByExternalID(context.Background(), constants.TablePermissionSetAssignment, constants.FieldPermissionSetAssignment_AssigneeID, "assignee-upsert", models.SObject{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-upsert",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-custom",
	}, permissionAdminUser())
	if err == nil {
		t.Fatal("expected upsert-create to fail")
	}
	if !strings.Contains(err.Error(), profileOwnedPermissionSetAssignmentMessage) {
		t.Fatalf("expected profile-owned assignment message, got %v", err)
	}
	if repo.insertCalls != 0 {
		t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
	}
}

func TestPersistenceUpdateRejectsBeforeSaveFlowRetargetIntoSystemProfileOwnedProfileTabSetting(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeUpdate {
			record[constants.FieldProfileTabSetting_ProfileID] = "profile-system"
		}
	}}

	err := ps.Update(context.Background(), constants.TableProfileTabSetting, "pts-custom", models.SObject{
		"Visibility": "Hidden",
	}, permissionAdminUser())
	assertPermissionResourceLocked(t, err)
	if repo.updateCalls != 0 {
		t.Fatalf("expected no repo update, got %d", repo.updateCalls)
	}
}

func TestPersistenceUpdateRecordsRejectsBeforeSaveFlowRetargetIntoSystemProfileOwnedPermissionSetAssignment(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType != sharedEvents.RecordBeforeUpdate {
			return
		}
		if recordID, _ := record[constants.FieldID].(string); recordID == "psa-system" {
			record[constants.FieldPermissionSetAssignment_PermissionSetID] = "ps-system"
		}
	}}

	results, err := ps.UpdateRecords(context.Background(), []models.SObject{{
		"attributes":      map[string]interface{}{"type": constants.TablePermissionSetAssignment},
		constants.FieldID: "psa-system",
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-next",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-custom",
	}}, false, permissionAdminUser())
	if err != nil {
		t.Fatalf("expected per-record failure, got err=%v", err)
	}
	if len(results) != 1 {
		t.Fatalf("expected one result, got %#v", results)
	}
	if len(results[0].Errors) != 1 {
		t.Fatalf("expected one per-record error, got %#v", results[0].Errors)
	}
	if !strings.Contains(results[0].Errors[0].Message, profileOwnedPermissionSetAssignmentMessage) {
		t.Fatalf("expected profile-owned assignment message, got %#v", results[0].Errors)
	}
	if repo.updateCalls != 0 {
		t.Fatalf("expected no repo update, got %d", repo.updateCalls)
	}
}

func TestCommitPreparedUpdateRejectsSystemProfileOwnedPermissionSetTabSetting(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	err := ps.CommitPreparedUpdate(context.Background(), constants.TablePermissionSetTabSetting, "psts-system", &models.PreparedRecordUpdate{
		Record: models.SObject{
			constants.FieldID: "psts-system",
			constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-system",
			constants.FieldPermissionSetTabSetting_TabName:         "Account",
			"Visibility": "Hidden",
		},
		EffectiveUpdates: models.SObject{
			"Visibility": "Hidden",
		},
	}, nil, permissionAdminUser())
	assertPermissionResourceLocked(t, err)
	if repo.updateCalls != 0 {
		t.Fatalf("expected no repo update, got %d", repo.updateCalls)
	}
}

func TestPersistenceInsertRejectsConnectedAppAccessWhenPermissionSetSideIsSystemOwned(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	_, err := ps.Insert(context.Background(), constants.TableConnectedAppAccess, models.SObject{
		constants.FieldSysConnectedAppAccess_ConnectedAppID:  "app-1",
		constants.FieldSysConnectedAppAccess_ProfileID:       "profile-custom",
		constants.FieldSysConnectedAppAccess_PermissionSetID: "ps-system",
	}, permissionAdminUser())
	assertPermissionResourceLocked(t, err)
	if repo.insertCalls != 0 {
		t.Fatalf("expected no repo insert, got %d", repo.insertCalls)
	}
}

func TestPersistenceBulkInsertRejectsSystemProfileOwnedPermissionSetTabSetting(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	result, err := ps.BulkInsert(context.Background(), constants.TablePermissionSetTabSetting, []models.SObject{{
		constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-system",
		constants.FieldPermissionSetTabSetting_TabName:         "Account",
		"Visibility": "Available",
	}}, permissionAdminUser(), BulkInsertOptions{})
	if err == nil {
		t.Fatal("expected bulk insert to fail")
	}
	if result.FailedCount != 1 || len(result.Errors) != 1 {
		t.Fatalf("expected one failed row, got %+v", result)
	}
	if !strings.Contains(result.Errors[0], systemProfileOwnedPermissionResourceLockedMessage) {
		t.Fatalf("expected locked message, got %+v", result.Errors)
	}
	if repo.bulkInsertCalls != 0 {
		t.Fatalf("expected no repo bulk insert, got %d", repo.bulkInsertCalls)
	}
}

func TestPersistenceBulkInsertRejectsSystemProfileOwnedPermissionSetTabSettingWhenSkipValidation(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	result, err := ps.BulkInsert(context.Background(), constants.TablePermissionSetTabSetting, []models.SObject{{
		constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-system",
		constants.FieldPermissionSetTabSetting_TabName:         "Account",
		"Visibility": "Available",
	}}, permissionAdminUser(), BulkInsertOptions{SkipValidation: true})
	if err == nil {
		t.Fatal("expected bulk insert to fail")
	}
	if result.FailedCount != 1 || len(result.Errors) != 1 {
		t.Fatalf("expected one failed row, got %+v", result)
	}
	if !strings.Contains(result.Errors[0], systemProfileOwnedPermissionResourceLockedMessage) {
		t.Fatalf("expected locked message, got %+v", result.Errors)
	}
	if repo.bulkInsertCalls != 0 {
		t.Fatalf("expected no repo bulk insert, got %d", repo.bulkInsertCalls)
	}
}

func TestPersistenceBulkInsertRejectsProfileOwnedPermissionSetAssignment(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	result, err := ps.BulkInsert(context.Background(), constants.TablePermissionSetAssignment, []models.SObject{{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-system",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-system",
	}}, permissionAdminUser(), BulkInsertOptions{})
	if err == nil {
		t.Fatal("expected bulk insert to fail")
	}
	if result.FailedCount != 1 || len(result.Errors) != 1 {
		t.Fatalf("expected one failed row, got %+v", result)
	}
	if !strings.Contains(result.Errors[0], profileOwnedPermissionSetAssignmentMessage) {
		t.Fatalf("expected profile-owned assignment message, got %+v", result.Errors)
	}
	if repo.bulkInsertCalls != 0 {
		t.Fatalf("expected no repo bulk insert, got %d", repo.bulkInsertCalls)
	}
}

func TestPersistenceBulkInsertRejectsProfileOwnedPermissionSetAssignmentWhenSkipValidation(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	result, err := ps.BulkInsert(context.Background(), constants.TablePermissionSetAssignment, []models.SObject{{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-system",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-system",
	}}, permissionAdminUser(), BulkInsertOptions{SkipValidation: true})
	if err == nil {
		t.Fatal("expected bulk insert to fail")
	}
	if result.FailedCount != 1 || len(result.Errors) != 1 {
		t.Fatalf("expected one failed row, got %+v", result)
	}
	if !strings.Contains(result.Errors[0], profileOwnedPermissionSetAssignmentMessage) {
		t.Fatalf("expected profile-owned assignment message, got %+v", result.Errors)
	}
	if repo.bulkInsertCalls != 0 {
		t.Fatalf("expected no repo bulk insert, got %d", repo.bulkInsertCalls)
	}
}

func TestPersistenceBulkInsertPermissionSetAssignmentLocksTargetPermissionSet(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	result, err := ps.BulkInsert(context.Background(), constants.TablePermissionSetAssignment, []models.SObject{{
		constants.FieldPermissionSetAssignment_AssigneeID:      "assignee-managed",
		constants.FieldPermissionSetAssignment_PermissionSetID: "ps-managed",
	}}, permissionAdminUser(), BulkInsertOptions{})
	if err != nil {
		t.Fatalf("BulkInsert PermissionSetAssignment: %v", err)
	}
	if result.SuccessCount != 1 || result.FailedCount != 0 {
		t.Fatalf("BulkInsert result = %+v, want one success", result)
	}
	if repo.bulkInsertCalls != 1 {
		t.Fatalf("repo bulk insert calls = %d, want 1", repo.bulkInsertCalls)
	}
	if len(repo.lockCalls) != 1 || repo.lockCalls[0] != constants.TablePermissionSet+":ps-managed" {
		t.Fatalf("lock calls = %#v, want PermissionSet:ps-managed", repo.lockCalls)
	}
}

func TestPersistenceInsertAllowsNonSystemPermissionResource(t *testing.T) {
	ps, repo := newPermissionResourceLockService(permissionResourceLockRows())

	_, err := ps.Insert(context.Background(), constants.TablePermissionSetTabSetting, models.SObject{
		constants.FieldPermissionSetTabSetting_PermissionSetID: "ps-custom",
		constants.FieldPermissionSetTabSetting_TabName:         "Account",
		"Visibility": "Available",
	}, permissionAdminUser())
	if err != nil {
		t.Fatalf("expected insert to pass, got %v", err)
	}
	if repo.insertCalls != 1 {
		t.Fatalf("expected one repo insert, got %d", repo.insertCalls)
	}
}
