package permissions

import (
	"bytes"
	"context"
	"fmt"
	"math"
	"slices"
	"strings"
	"testing"

	"github.com/nexuscrm/kernel/pkg/schemacatalog"
	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

func TestAssignmentPolicyTrustedSuspendAndResumeReconcileWhileProtected(t *testing.T) {
	_, lifecycle, err := newAssignmentPolicyTestManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Lifecycle().Mutate(context.Background(), cp.ComponentManagedScopeItem, item, plan, store); err != nil {
		t.Fatal(err)
	}
	provider := assignmentPolicyTestTransactionOperationProvider()
	suspendPlan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator", Reason: "incident",
	})
	if err != nil {
		t.Fatal(err)
	}
	assertAssignmentPolicySourceTarget(t, suspendPlan, "app", "sales_users")
	if len(suspendPlan.Targets()) != 3 {
		t.Fatalf("suspend targets = %#v", suspendPlan.Targets())
	}
	assertAssignmentPolicyManagedTarget(t, suspendPlan, PermissionSetAssignmentPolicySerializationTarget())
	originalAssignmentID := assignmentPolicyTestAssignment(store, "user-sales")["Id"]
	if _, err := provider.Mutate(context.Background(), suspendPlan, store); err != nil {
		t.Fatal(err)
	}
	deletedAssignment := assignmentPolicyTestAssignment(store, "user-sales")
	if deletedAssignment["IsDeleted"] != true {
		t.Fatalf("suspension retained assignment: %#v", deletedAssignment)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	receipts := store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable]
	if len(receipts) != 1 || receipts[0]["AssignmentId"] != originalAssignmentID ||
		receipts[0]["PermissionSetId"] != "ps-sales" || receipts[0]["AssigneeId"] != "user-sales" ||
		receipts[0]["OwnerPack"] != "app" || receipts[0]["PolicyKey"] != "sales_users" ||
		receipts[0]["TombstonedGeneration"] != state["WorkGeneration"] ||
		receipts[0]["TombstonedAt"] != deletedAssignment["LastModifiedDate"] {
		t.Fatalf("suspension tombstone receipts = %#v, state = %#v", receipts, state)
	}
	suspended, err := provider.Project(context.Background(), suspendPlan, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(suspended.Expected, suspended.Live) {
		t.Fatalf("suspended projection differs:\n%s\n%s", suspended.Expected, suspended.Live)
	}

	resumePlan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationResume, Actor: "operator",
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := provider.Mutate(context.Background(), resumePlan, store); err != nil {
		t.Fatal(err)
	}
	restoredAssignment := assignmentPolicyTestAssignment(store, "user-sales")
	if restoredAssignment["Id"] != originalAssignmentID || restoredAssignment["IsDeleted"] != false {
		t.Fatalf("resume did not reconcile before releasing suspension: %#v", restoredAssignment)
	}
	if receipts := store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable]; len(receipts) != 0 {
		t.Fatalf("resume did not consume exact tombstone receipt: %#v", receipts)
	}
	resumed, err := provider.Project(context.Background(), resumePlan, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(resumed.Expected, resumed.Live) {
		t.Fatalf("resumed projection differs:\n%s\n%s", resumed.Expected, resumed.Live)
	}
}

func TestAssignmentPolicyCrossScopePlansShareCanonicalTargetOrder(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	itemPlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, item, itemPlan, store,
	); err != nil {
		t.Fatal(err)
	}

	setRequest, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, []string{"sales_users"})
	if err != nil {
		t.Fatal(err)
	}
	setPlan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	operatorPlan, err := planAssignmentPolicyTestOperation(
		context.Background(), store, AssignmentPolicyOperationRequest{
			PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
			Actor: "operator", Reason: "incident",
		},
	)
	if err != nil {
		t.Fatal(err)
	}
	targetKey := func(target cp.ComponentManagedTarget) string {
		return target.TargetDomain + "\x00" + string(target.PhysicalKeyBytes)
	}
	commonKeys := func(left, right cp.ComponentManagedPlan) []string {
		rightSet := make(map[string]struct{}, len(right.Targets()))
		for _, target := range right.Targets() {
			rightSet[targetKey(target)] = struct{}{}
		}
		common := make([]string, 0, len(left.Targets()))
		for _, target := range left.Targets() {
			key := targetKey(target)
			if _, ok := rightSet[key]; ok {
				common = append(common, key)
			}
		}
		return common
	}
	operatorOrder := commonKeys(operatorPlan, setPlan)
	setOrder := commonKeys(setPlan, operatorPlan)
	if !slices.Equal(operatorOrder, setOrder) {
		t.Fatalf("operator/set common target order differs: operator=%q set=%q", operatorOrder, setOrder)
	}
	globalKey := targetKey(PermissionSetAssignmentPolicySerializationTarget())
	if !slices.Contains(operatorOrder, globalKey) {
		t.Fatalf("operator/set plans do not share the global serialization target: %q", operatorOrder)
	}
}

func TestAssignmentPolicyInvalidResumeCommitsSafeEmptyAndRetainsSuspension(t *testing.T) {
	_, lifecycle, err := newAssignmentPolicyTestManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Lifecycle().Mutate(context.Background(), cp.ComponentManagedScopeItem, item, plan, store); err != nil {
		t.Fatal(err)
	}
	provider := assignmentPolicyTestTransactionOperationProvider()
	suspendPlan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator", Reason: "incident",
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := provider.Mutate(context.Background(), suspendPlan, store); err != nil {
		t.Fatal(err)
	}
	delete(store.rows, "Profile")
	resumePlan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationResume, Actor: "operator",
	})
	if err != nil {
		t.Fatal(err)
	}
	result, err := provider.Mutate(context.Background(), resumePlan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationSafeProjectionStaged {
		t.Fatalf("resume disposition = %q", result.Disposition)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	if state["Suspended"] != true || state["ResolutionState"] != assignmentPolicyResolutionInvalidSelector {
		t.Fatalf("safe-invalid resume state = %#v", state)
	}
	if row := assignmentPolicyTestAssignment(store, "user-sales"); row["IsDeleted"] != true {
		t.Fatalf("safe-invalid resume granted assignment: %#v", row)
	}
	projection, err := provider.Project(context.Background(), resumePlan, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("safe-invalid resume projection differs:\n%s\n%s", projection.Expected, projection.Live)
	}
}

func TestAssignmentPolicyResumeWithMissingSelectorsCommitsSafeEmptyAndRetainsSuspension(t *testing.T) {
	_, lifecycle, err := newAssignmentPolicyTestManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Lifecycle().Mutate(context.Background(), cp.ComponentManagedScopeItem, item, plan, store); err != nil {
		t.Fatal(err)
	}
	provider := assignmentPolicyTestTransactionOperationProvider()
	suspendPlan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator", Reason: "incident",
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := provider.Mutate(context.Background(), suspendPlan, store); err != nil {
		t.Fatal(err)
	}

	delete(store.rows, schemacatalog.AssignmentPolicySelectorTable)
	resumePlan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationResume, Actor: "operator",
	})
	if err != nil {
		t.Fatal(err)
	}
	result, err := provider.Mutate(context.Background(), resumePlan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationSafeProjectionStaged {
		t.Fatalf("resume disposition = %q", result.Disposition)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	if state["Suspended"] != true || state["ResolutionState"] != assignmentPolicyResolutionInvalidSelector {
		t.Fatalf("missing-selector resume state = %#v", state)
	}
	if row := assignmentPolicyTestAssignment(store, "user-sales"); row["IsDeleted"] != true {
		t.Fatalf("missing-selector resume granted assignment: %#v", row)
	}
	projection, err := provider.Project(context.Background(), resumePlan, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("missing-selector resume projection differs:\n%s\n%s", projection.Expected, projection.Live)
	}
}

func TestAssignmentPolicyOperationAttributionIsCanonicalAndBounded(t *testing.T) {
	validActor := strings.Repeat("a", assignmentPolicyOperationMaximumActorBytes)
	validReason := strings.Repeat("r", assignmentPolicyOperationMaximumReasonBytes)
	if err := validateAssignmentPolicyOperationRequest(AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: validActor, Reason: validReason,
	}); err != nil {
		t.Fatalf("maximum attribution rejected: %v", err)
	}

	invalidUTF8 := string([]byte{0xff})
	for _, test := range []struct {
		name    string
		request AssignmentPolicyOperationRequest
	}{
		{
			name: "empty suspend actor",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend, Reason: "incident",
			},
		},
		{
			name: "empty suspend reason",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend, Actor: "operator",
			},
		},
		{
			name: "actor surrounding whitespace",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
				Actor: " operator", Reason: "incident",
			},
		},
		{
			name: "reason surrounding whitespace",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
				Actor: "operator", Reason: "incident\n",
			},
		},
		{
			name: "actor too long",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
				Actor: validActor + "a", Reason: "incident",
			},
		},
		{
			name: "reason too long",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
				Actor: "operator", Reason: validReason + "r",
			},
		},
		{
			name: "invalid UTF-8",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
				Actor: invalidUTF8, Reason: "incident",
			},
		},
		{
			name: "resume reason",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationResume,
				Actor: "operator", Reason: "not accepted",
			},
		},
		{
			name: "reconcile attribution",
			request: AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationReconcile,
				Actor: "operator",
			},
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			if err := validateAssignmentPolicyOperationRequest(test.request); err == nil {
				t.Fatal("invalid attribution was accepted")
			}
		})
	}
}

func TestAssignmentPolicyOperationRejectsResumeUnlessAlreadySuspended(t *testing.T) {
	store, _ := assignmentPolicyOperationFixture(t)
	_, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationResume, Actor: "operator",
	})
	if err == nil || !strings.Contains(err.Error(), "not suspended") {
		t.Fatalf("resume error = %v", err)
	}
}

func TestAssignmentPolicyOperationRepeatedSuspendPreservesOriginalAttribution(t *testing.T) {
	store, provider := assignmentPolicyOperationFixture(t)
	first, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator-1", Reason: "incident-1",
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := provider.Mutate(context.Background(), first, store); err != nil {
		t.Fatal(err)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	originalActor, originalReason, originalAt :=
		state["SuspensionActor"], state["SuspensionReason"], state["SuspendedAt"]

	second, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator-2", Reason: "incident-2",
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(second.Effects()) != 0 {
		t.Fatalf("repeated suspend effects = %#v", second.Effects())
	}
	result, err := provider.Mutate(context.Background(), second, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationNoChange {
		t.Fatalf("repeated suspend disposition = %q", result.Disposition)
	}
	if state["SuspensionActor"] != originalActor || state["SuspensionReason"] != originalReason ||
		state["SuspendedAt"] != originalAt {
		t.Fatalf("repeated suspend replaced attribution: %#v", state)
	}
}

func TestAssignmentPolicyOperationConvergesPendingGenerationWithoutAdvancingWork(t *testing.T) {
	store, provider := assignmentPolicyOperationFixture(t)
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	state["WorkGeneration"] = uint64(7)
	state["AppliedGeneration"] = uint64(6)
	plan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationReconcile,
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(plan.Effects()) != 0 {
		t.Fatalf("pending-generation catch-up effects = %#v", plan.Effects())
	}
	decoded, err := decodeAssignmentPolicyOperationPlan(plan)
	if err != nil {
		t.Fatal(err)
	}
	if decoded.FinalGeneration != 7 || decoded.AssignmentWrites != 0 || !decoded.StateWrite {
		t.Fatalf("pending-generation plan = %#v", decoded)
	}
	if _, err := provider.Mutate(context.Background(), plan, store); err != nil {
		t.Fatal(err)
	}
	if state["WorkGeneration"] != uint64(7) || state["AppliedGeneration"] != uint64(7) {
		t.Fatalf("pending generation not converged in place: %#v", state)
	}
}

func TestAssignmentPolicyOperationGenerationExhaustionAllowsOnlyExactNoOp(t *testing.T) {
	store, provider := assignmentPolicyOperationFixture(t)
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	state["WorkGeneration"] = uint64(math.MaxUint64)
	state["AppliedGeneration"] = uint64(math.MaxUint64)
	plan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationReconcile,
	})
	if err != nil {
		t.Fatalf("exact no-op at maximum generation: %v", err)
	}
	result, err := provider.Mutate(context.Background(), plan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationNoChange {
		t.Fatalf("maximum-generation no-op disposition = %q", result.Disposition)
	}
	_, err = planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator", Reason: "incident",
	})
	if err == nil || !strings.Contains(err.Error(), "exhausted") {
		t.Fatalf("maximum-generation mutation error = %v", err)
	}
}

func TestAssignmentPolicyOperationPreflightDriftFailsBeforeWrites(t *testing.T) {
	for _, test := range []struct {
		name      string
		stage     func(*permissionSetDefinitionLifecycleMemoryStore)
		wantError string
	}{
		{
			name: "claim row replaced",
			stage: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				store.rows[schemacatalog.AssignmentPolicyClaimTable][0]["Id"] = "replacement-claim"
			},
			wantError: "authority changed",
		},
		{
			name: "state changed",
			stage: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]["WorkGeneration"] = uint64(99)
			},
			wantError: "state changed",
		},
		{
			name: "selector changed",
			stage: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				store.rows[schemacatalog.AssignmentPolicySelectorTable][0]["ProfileName"] = "Support"
			},
			wantError: "selector population changed",
		},
		{
			name: "assignment population grew",
			stage: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				store.rows["PermissionSetAssignment"] = append(store.rows["PermissionSetAssignment"], cp.ComponentRow{
					"Id": "direct-assignment", "PermissionSetId": "ps-sales", "AssigneeId": "user-direct",
					"IsDeleted": false, "LastModifiedDate": "2026-08-15T00:00:00Z",
				})
			},
			wantError: "population grew",
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			store, provider := assignmentPolicyOperationFixture(t)
			plan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
				PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
				Actor: "operator", Reason: "incident",
			})
			if err != nil {
				t.Fatal(err)
			}
			writesBefore := assignmentPolicyOperationWriteCount(store)
			originalAssignment := assignmentPolicyTestAssignment(store, "user-sales")
			originalDeleted := originalAssignment["IsDeleted"]
			test.stage(store)
			_, err = provider.Mutate(context.Background(), plan, store)
			if err == nil || !strings.Contains(err.Error(), test.wantError) {
				t.Fatalf("preflight error = %v", err)
			}
			if got := assignmentPolicyOperationWriteCount(store); got != writesBefore {
				t.Fatalf("writes after preflight failure = %d, want %d", got, writesBefore)
			}
			if originalAssignment["IsDeleted"] != originalDeleted {
				t.Fatalf("preflight failure mutated original assignment: %#v", originalAssignment)
			}
		})
	}
}

func TestAssignmentPolicyOperationEligibilityDriftCannotRestoreAssignment(t *testing.T) {
	store, provider := assignmentPolicyOperationFixture(t)
	suspendPlan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator", Reason: "incident",
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := provider.Mutate(context.Background(), suspendPlan, store); err != nil {
		t.Fatal(err)
	}
	resumePlan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationResume, Actor: "operator",
	})
	if err != nil {
		t.Fatal(err)
	}
	writesBefore := assignmentPolicyOperationWriteCount(store)
	store.rows["User"][0]["IsActive"] = false
	_, err = provider.Mutate(context.Background(), resumePlan, store)
	if err == nil || !strings.Contains(err.Error(), "eligibility population changed") {
		t.Fatalf("eligibility drift error = %v", err)
	}
	if got := assignmentPolicyOperationWriteCount(store); got != writesBefore {
		t.Fatalf("writes after eligibility drift = %d, want %d", got, writesBefore)
	}
	if assignment := assignmentPolicyTestAssignment(store, "user-sales"); assignment["IsDeleted"] != true {
		t.Fatalf("eligibility drift restored an ineligible assignment: %#v", assignment)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	if state["Suspended"] != true {
		t.Fatalf("eligibility drift released suspension: %#v", state)
	}
}

func TestAssignmentPolicyOperationMutateNeverLocksProfileOrUser(t *testing.T) {
	store, provider := assignmentPolicyOperationFixture(t)
	plan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator", Reason: "incident",
	})
	if err != nil {
		t.Fatal(err)
	}
	guard := &assignmentPolicyOperationNoSourceLockStore{permissionSetDefinitionLifecycleMemoryStore: store}
	if _, err := provider.Mutate(context.Background(), plan, guard); err != nil {
		t.Fatal(err)
	}
}

func TestAssignmentPolicyOperationRepairRevokesDirectAssignmentByPolicyAuthority(t *testing.T) {
	store, provider := assignmentPolicyOperationFixture(t)
	store.rows["PermissionSetAssignment"] = append(store.rows["PermissionSetAssignment"], cp.ComponentRow{
		"Id": "direct-assignment", "PermissionSetId": "ps-sales", "AssigneeId": "user-direct",
		"IsDeleted": false, "LastModifiedDate": "2026-08-15T00:00:00Z",
	})
	plan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationRepair,
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := provider.Mutate(context.Background(), plan, store); err != nil {
		t.Fatal(err)
	}
	direct := assignmentPolicyTestAssignment(store, "user-direct")
	if direct == nil || direct["IsDeleted"] != true {
		t.Fatalf("direct assignment escaped policy authority: %#v", direct)
	}
	receipts := store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable]
	if len(receipts) != 1 || receipts[0]["AssignmentId"] != "direct-assignment" ||
		receipts[0]["AssigneeId"] != "user-direct" || receipts[0]["OwnerPack"] != "app" ||
		receipts[0]["PolicyKey"] != "sales_users" {
		t.Fatalf("direct-assignment tombstone provenance = %#v", receipts)
	}
	projection, err := provider.Project(context.Background(), plan, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("repair projection differs:\nexpected=%s\nlive=%s", projection.Expected, projection.Live)
	}
}

func TestAssignmentPolicyOperationEstimateAccountsForWholeManagedAttempt(t *testing.T) {
	store, _ := assignmentPolicyOperationFixture(t)
	plan, err := planAssignmentPolicyTestOperation(context.Background(), store, AssignmentPolicyOperationRequest{
		PermissionSet: "Sales_User", Operation: AssignmentPolicyOperationSuspend,
		Actor: "operator", Reason: "incident",
	})
	if err != nil {
		t.Fatal(err)
	}
	snapshot, err := readAssignmentPolicyOperationSnapshot(
		context.Background(),
		store,
		"Sales_User",
		assignmentPolicyManagedBudget().MaxCurrentItems,
		assignmentPolicyTestEligibilityProvider(),
	)
	if err != nil {
		t.Fatal(err)
	}
	if plan.Estimate().CurrentItems <= 2*snapshot.CurrentItems {
		t.Fatalf("attempt estimate %#v does not include post-mutation projection", plan.Estimate())
	}
	overBudget := cp.ComponentManagedUsage{CurrentItems: assignmentPolicyManagedBudget().MaxCurrentItems + 1}
	if err := validateAssignmentPolicyOperationEstimate(overBudget); err == nil {
		t.Fatal("over-budget synthetic attempt was accepted")
	}
}

func assignmentPolicyOperationFixture(
	t *testing.T,
) (*permissionSetDefinitionLifecycleMemoryStore, cp.ComponentManagedTransactionOperationProvider) {
	t.Helper()
	_, lifecycle, err := newAssignmentPolicyTestManagedLifecycleRegistration()
	if err != nil {
		t.Fatal(err)
	}
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Lifecycle().Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Lifecycle().Mutate(
		context.Background(), cp.ComponentManagedScopeItem, item, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	return store, assignmentPolicyTestTransactionOperationProvider()
}

func assignmentPolicyOperationWriteCount(store *permissionSetDefinitionLifecycleMemoryStore) int {
	return len(store.inserts) + len(store.updates) + len(store.deletes)
}

type assignmentPolicyOperationNoSourceLockStore struct {
	*permissionSetDefinitionLifecycleMemoryStore
}

func (s *assignmentPolicyOperationNoSourceLockStore) LockRows(
	ctx context.Context,
	query cp.ComponentManagedRowQuery,
) ([]cp.ComponentRow, error) {
	if query.Table == "Profile" || query.Table == "User" {
		return nil, fmt.Errorf("operation attempted forbidden source lock on %s", query.Table)
	}
	return s.permissionSetDefinitionLifecycleMemoryStore.LockRows(ctx, query)
}

func (s *assignmentPolicyOperationNoSourceLockStore) LockRowsKeysetPage(
	ctx context.Context,
	page cp.ComponentManagedKeysetRowQuery,
) (cp.ComponentManagedKeysetRowPage, error) {
	if page.Query.Table == "Profile" || page.Query.Table == "User" {
		return cp.ComponentManagedKeysetRowPage{}, fmt.Errorf(
			"operation attempted forbidden source lock on %s", page.Query.Table,
		)
	}
	return s.permissionSetDefinitionLifecycleMemoryStore.LockRowsKeysetPage(ctx, page)
}
