package permissions

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"math"
	"sort"
	"strings"
	"testing"

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

func TestAssignmentPolicyLifecycleFirstClaimExactReplacesAndReactivatesTombstone(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	store.rows["PermissionSetAssignment"] = []cp.ComponentRow{
		{"Id": "manual", "PermissionSetId": "ps-sales", "AssigneeId": "user-old", "IsDeleted": false},
		{
			"Id": "stable-tombstone", "PermissionSetId": "ps-sales", "AssigneeId": "user-sales",
			"IsDeleted": true, "LastModifiedDate": "2026-08-12T10:00:00Z",
		},
	}
	store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable] = []cp.ComponentRow{{
		"Id": "receipt-stable", "AssignmentId": "stable-tombstone",
		"PermissionSetId": "ps-sales", "AssigneeId": "user-sales",
		"OwnerPack": "app", "PolicyKey": "sales_users",
		"TombstonedGeneration": uint64(7), "TombstonedAt": "2026-08-12T10:00:00Z",
	}}
	store.rows[schemacatalog.AssignmentPolicyTargetStateTable] = []cp.ComponentRow{{
		"Id": "state-stable", "TargetDomain": permissionSetDefinitionManagedTargetDomain,
		"PhysicalKey": []byte("sales_user"), "PermissionSetId": "ps-sales",
		"Suspended": false, "WorkGeneration": uint64(7), "AppliedGeneration": uint64(7),
		"ResolutionState": assignmentPolicyResolutionResolved,
	}}
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))

	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	assertAssignmentPolicySourceTarget(t, plan, "app", "sales_users")
	assertAssignmentPolicyManagedTarget(t, plan, assignmentPolicySetTarget("app"))
	assertAssignmentPolicyManagedTarget(t, plan, PermissionSetAssignmentPolicySerializationTarget())
	selectorTarget, err := assignmentPolicySelectorManagedTarget("Sales")
	if err != nil {
		t.Fatal(err)
	}
	assertAssignmentPolicyManagedTarget(t, plan, selectorTarget)
	if got := len(plan.Targets()); got != 5 {
		t.Fatalf("first-claim targets = %#v, want global serialization, pack set, logical source, selector, and physical Definition", plan.Targets())
	}
	result, err := lifecycle.Mutate(context.Background(), cp.ComponentManagedScopeItem, request, plan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationDesiredProjectionStaged {
		t.Fatalf("disposition = %q", result.Disposition)
	}
	if row := assignmentPolicyTestAssignment(store, "user-old"); row == nil || row["IsDeleted"] != true {
		t.Fatalf("historical assignment was not revoked: %#v", row)
	}
	if row := assignmentPolicyTestAssignment(store, "user-sales"); row == nil ||
		row["Id"] != "stable-tombstone" || row["IsDeleted"] != false {
		t.Fatalf("desired tombstone was not reactivated in place: %#v", row)
	}
	if rows := store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable]; len(rows) != 1 ||
		rows[0]["AssignmentId"] != "manual" || rows[0]["PermissionSetId"] != "ps-sales" ||
		rows[0]["AssigneeId"] != "user-old" || rows[0]["OwnerPack"] != "app" ||
		rows[0]["PolicyKey"] != "sales_users" || rows[0]["TombstonedGeneration"] != uint64(8) {
		t.Fatalf("tombstone receipts = %#v", rows)
	}
	projection, err := lifecycle.Project(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("projection differs:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
}

func TestAssignmentPolicyLifecycleDeclarationApplySynchronouslyGrantsEligibleAssignments(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	result, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationDesiredProjectionStaged {
		t.Fatalf("declaration apply disposition = %q", result.Disposition)
	}
	assignment := assignmentPolicyTestAssignment(store, "user-sales")
	if assignment == nil || assignment["IsDeleted"] != false {
		t.Fatalf("declaration apply did not synchronously grant eligible assignment: %#v", assignment)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	if state["WorkGeneration"] != uint64(1) || state["AppliedGeneration"] != uint64(1) {
		t.Fatalf("synchronous declaration apply retained pending work: %#v", state)
	}
}

func TestAssignmentPolicyLifecycleSerializesItemAndExactSetTargets(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)
	}
	assertAssignmentPolicyManagedTarget(t, itemPlan, assignmentPolicySetTarget("app"))
	assertAssignmentPolicyManagedTarget(t, itemPlan, PermissionSetAssignmentPolicySerializationTarget())
	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", "support_users"})
	if err != nil {
		t.Fatal(err)
	}
	setPlan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	assertAssignmentPolicyManagedTarget(t, setPlan, assignmentPolicySetTarget("app"))
	assertAssignmentPolicyManagedTarget(t, setPlan, PermissionSetAssignmentPolicySerializationTarget())
	assertAssignmentPolicySourceTarget(t, setPlan, "app", "sales_users")
	assertAssignmentPolicySourceTarget(t, setPlan, "app", "support_users")
	definitionTarget, err := PermissionSetDefinitionManagedTarget("Sales_User")
	if err != nil {
		t.Fatal(err)
	}
	assertAssignmentPolicyManagedTarget(t, setPlan, definitionTarget)
	if got := len(setPlan.Targets()); got != 5 {
		t.Fatalf("exact-set targets = %#v, want global serialization, pack set, both sources, and retained physical Definition", setPlan.Targets())
	}
}

func TestAssignmentPolicyLifecycleExactSetPlanFingerprintIsDeterministic(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)
	}

	request, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, []string{
		"sales_users", "support_users", "finance_users", "service_users",
		"marketing_users", "analytics_users", "developer_users", "security_users",
	})
	if err != nil {
		t.Fatal(err)
	}
	first, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, request, store)
	if err != nil {
		t.Fatal(err)
	}
	for attempt := 0; attempt < 128; attempt++ {
		plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, request, store)
		if err != nil {
			t.Fatal(err)
		}
		if !bytes.Equal(plan.Fingerprint(), first.Fingerprint()) {
			t.Fatalf("unchanged exact-set plan fingerprint differs on attempt %d", attempt)
		}
		result, err := lifecycle.Mutate(
			context.Background(), cp.ComponentManagedScopeSet, request, plan, store,
		)
		if err != nil {
			t.Fatalf("unchanged exact-set mutate failed on attempt %d: %v", attempt, err)
		}
		if result.Disposition != cp.ComponentManagedMutationNoChange {
			t.Fatalf("unchanged exact-set disposition on attempt %d = %q", attempt, result.Disposition)
		}
	}
}

func TestAssignmentPolicyLifecyclePermissionSetCaseAliasesShareTargetState(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	store.rows["PermissionSet"][0]["Name"] = "sales_user"

	for _, name := range []string{"Sales_User", "SALES_USER"} {
		request := cp.NewComponentManagedItemLifecycleRequest(
			assignmentPolicyManagedRequestFor(t, "app", "sales_users", name),
		)
		plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, request, store)
		if err != nil {
			t.Fatalf("plan PermissionSet alias %q: %v", name, err)
		}
		if _, err := lifecycle.Mutate(
			context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
		); err != nil {
			t.Fatalf("mutate PermissionSet alias %q: %v", name, err)
		}
	}

	states := store.rows[schemacatalog.AssignmentPolicyTargetStateTable]
	if len(states) != 1 || !bytes.Equal(assignmentPolicyBytes(states[0]["PhysicalKey"]), []byte("sales_user")) {
		t.Fatalf("PermissionSet case aliases did not share target state: %#v", states)
	}
}

func TestAssignmentPolicyLifecycleRejectsPermissionSetAccentAliasOutsideManagedDomain(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	payload := []byte(`{
		"schema":"nexuscrm.permission-set-assignment-policy/v1",
		"permissionSet":"Café_User",
		"profiles_any_of":["Sales"]
	}`)
	request := cp.NewComponentManagedItemLifecycleRequest(cp.ComponentManagedRequest{
		PackName: "app",
		Component: cp.ManifestComponent{
			Kind: PermissionSetAssignmentPolicyComponentKind, BusinessKey: "cafe_users",
			Manageability: cp.ManageabilityLocked,
		},
		Payload: payload, ExpectedProjection: []byte(`{}`),
	})
	if _, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, assignmentPolicyLifecycleStore(),
	); err == nil || !strings.Contains(err.Error(), "Permission Set name") {
		t.Fatalf("accent-equivalent PermissionSet request planning error = %v", err)
	}
}

func TestAssignmentPolicyLifecycleMutateValidatesEnvelopeBeforeDispatch(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	setRequest, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name    string
		request cp.ComponentManagedLifecycleRequest
		plan    cp.ComponentManagedPlan
		store   cp.ComponentManagedMutationStore
		want    string
	}{
		{name: "nil store", request: item, plan: plan, want: "mutation store"},
		{name: "missing item request", plan: plan, store: store, want: "request"},
		{name: "scope-mismatched set request", request: setRequest, plan: plan, store: store, want: "item lifecycle request"},
		{name: "invalid plan", request: item, store: store, want: "plan"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			_, err := lifecycle.Mutate(
				context.Background(), cp.ComponentManagedScopeItem,
				test.request, test.plan, test.store,
			)
			if err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("mutation validation error = %v, want %q", err, test.want)
			}
		})
	}
}

func TestAssignmentPolicyLifecycleProjectionPreservesAssigneeIDCasing(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	store.rows["User"][0]["Id"] = "005AbC"
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) ||
		!bytes.Contains(projection.Live, []byte(`"005AbC"`)) {
		t.Fatalf("mixed-case assignee projection differs:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
}

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

	changed := assignmentPolicyManagedRequest(t)
	changed.PackName = "App"
	changed.Component.BusinessKey = "Sales_Users"
	policy, err := DecodePermissionSetAssignmentPolicy(changed.Payload)
	if err != nil {
		t.Fatal(err)
	}
	changed.ExpectedProjection, err = json.Marshal(assignmentPolicyDeclarationProjection{
		Present: true, PolicyKey: "Sales_Users", OwnerPack: "App",
		PermissionSet: policy.PermissionSet, ProfilesAnyOf: policy.ProfilesAnyOf,
	})
	if err != nil {
		t.Fatal(err)
	}
	request := cp.NewComponentManagedItemLifecycleRequest(changed)
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("case-only source projection differs:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
	claims := store.rows[schemacatalog.AssignmentPolicyClaimTable]
	if len(claims) != 1 || claims[0]["OwnerPack"] != "App" || claims[0]["PolicyKey"] != "Sales_Users" {
		t.Fatalf("case-only source change did not retain one canonical claim: %#v", claims)
	}
}

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

	changed := assignmentPolicyManagedRequest(t)
	policy, err := DecodePermissionSetAssignmentPolicy(changed.Payload)
	if err != nil {
		t.Fatal(err)
	}
	policy.ProfilesAnyOf = []string{"sales"}
	changed.Payload, err = json.Marshal(policy)
	if err != nil {
		t.Fatal(err)
	}
	changed.ExpectedProjection, err = json.Marshal(assignmentPolicyDeclarationProjection{
		Present: true, PolicyKey: changed.Component.BusinessKey, OwnerPack: changed.PackName,
		PermissionSet: policy.PermissionSet, ProfilesAnyOf: policy.ProfilesAnyOf,
	})
	if err != nil {
		t.Fatal(err)
	}
	request := cp.NewComponentManagedItemLifecycleRequest(changed)
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	selectors := store.rows[schemacatalog.AssignmentPolicySelectorTable]
	if len(selectors) != 1 || selectors[0]["ProfileName"] != "sales" {
		t.Fatalf("case-only selector spelling was not updated: %#v", selectors)
	}
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("case-only selector projection differs:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
}

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

	changed := assignmentPolicyManagedRequest(t)
	policy, err := DecodePermissionSetAssignmentPolicy(changed.Payload)
	if err != nil {
		t.Fatal(err)
	}
	policy.ProfilesAnyOf = []string{"Standard"}
	changed.Payload, err = json.Marshal(policy)
	if err != nil {
		t.Fatal(err)
	}
	changed.ExpectedProjection, err = json.Marshal(assignmentPolicyDeclarationProjection{
		Present: true, PolicyKey: changed.Component.BusinessKey, OwnerPack: changed.PackName,
		PermissionSet: policy.PermissionSet, ProfilesAnyOf: policy.ProfilesAnyOf,
	})
	if err != nil {
		t.Fatal(err)
	}
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem,
		cp.NewComponentManagedItemLifecycleRequest(changed), store,
	)
	if err != nil {
		t.Fatal(err)
	}
	for _, selector := range []string{"Sales", "Standard"} {
		target, err := assignmentPolicySelectorManagedTarget(selector)
		if err != nil {
			t.Fatal(err)
		}
		assertAssignmentPolicyManagedTarget(t, plan, target)
	}
}

func TestAssignmentPolicyLifecycleProjectionRejectsMissingDesiredClaim(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	delete(store.rows, schemacatalog.AssignmentPolicyClaimTable)
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("missing desired claim was accepted:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
	if !bytes.Contains(projection.Expected, []byte(`"present":true`)) ||
		!bytes.Contains(projection.Live, []byte(`"present":false`)) {
		t.Fatalf("missing-claim presence proof is not explicit:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
}

func TestAssignmentPolicyLifecycleInvalidSelectorConvergesExactEmpty(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	delete(store.rows, "Profile")
	store.rows["PermissionSetAssignment"] = []cp.ComponentRow{{
		"Id": "manual", "PermissionSetId": "ps-sales", "AssigneeId": "user-old", "IsDeleted": false,
	}}
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	result, err := lifecycle.Mutate(context.Background(), cp.ComponentManagedScopeItem, request, plan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationSafeProjectionStaged {
		t.Fatalf("disposition = %q", result.Disposition)
	}
	if row := assignmentPolicyTestAssignment(store, "user-old"); row["IsDeleted"] != true {
		t.Fatalf("invalid selector retained active assignment: %#v", row)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	if state["ResolutionState"] != assignmentPolicyResolutionInvalidSelector {
		t.Fatalf("resolution = %v", state["ResolutionState"])
	}
}

func TestAssignmentPolicyLifecycleMultipleNegativeEdgesConvergeExactEmpty(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	store.rows["PermissionSetTabSetting"] = []cp.ComponentRow{
		{"Id": "tab-a", "PermissionSetId": "ps-sales"},
		{"Id": "tab-b", "PermissionSetId": "ps-sales"},
	}
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	result, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationSafeProjectionStaged {
		t.Fatalf("negative-edge disposition = %q", result.Disposition)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	if state["ResolutionState"] != assignmentPolicyResolutionUnsupportedDrift {
		t.Fatalf("negative-edge resolution = %#v", state)
	}
}

func TestAssignmentPolicyLifecycleRemovalClearsBeforeClaimReleaseAndRetainsSuspension(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(context.Background(), cp.ComponentManagedScopeItem, item, plan, store); err != nil {
		t.Fatal(err)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	state["Suspended"] = true
	state["SuspensionActor"] = "operator-1"
	state["SuspensionReason"] = "incident"
	state["SuspendedAt"] = "2026-08-12T10:00:00Z"
	setRequest, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, setRequest, store)
	if err != nil {
		t.Fatal(err)
	}
	assertAssignmentPolicySourceTarget(t, removePlan, "app", "sales_users")
	result, err := lifecycle.Mutate(context.Background(), cp.ComponentManagedScopeSet, setRequest, removePlan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationSafeProjectionStaged {
		t.Fatalf("disposition = %q", result.Disposition)
	}
	if len(store.rows[schemacatalog.AssignmentPolicyClaimTable]) != 0 {
		t.Fatal("claim retained after exact-empty removal")
	}
	states := store.rows[schemacatalog.AssignmentPolicyTargetStateTable]
	if len(states) != 1 {
		t.Fatalf("target state rows = %#v, want retained tombstone", states)
	}
	state = states[0]
	if state["Suspended"] != true || state["SuspensionActor"] != "operator-1" ||
		state["SuspensionReason"] != "incident" || state["SuspendedAt"] != "2026-08-12T10:00:00Z" {
		t.Fatalf("removal erased target-scoped suspension: %#v", state)
	}
	if state["ResolutionState"] != assignmentPolicyResolutionRemoved ||
		state["WorkGeneration"] != uint64(2) || state["AppliedGeneration"] != uint64(2) ||
		state["NextAttemptAt"] != nil || state["RetryCount"] != uint64(0) {
		t.Fatalf("removed target state = %#v", state)
	}
	for _, deletion := range store.deletes {
		if deletion.Table == schemacatalog.AssignmentPolicyTargetStateTable {
			t.Fatalf("removal physically deleted target state: %#v", deletion)
		}
	}
	receipts := store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable]
	if len(receipts) != 1 || receipts[0]["OwnerPack"] != "app" ||
		receipts[0]["PolicyKey"] != "sales_users" ||
		receipts[0]["PermissionSetId"] != "ps-sales" ||
		len(assignmentPolicyBytes(receipts[0]["ExactEmptyDigest"])) != 32 {
		t.Fatalf("release receipt = %#v", receipts)
	}
	target, err := PermissionSetDefinitionManagedTarget("Sales_User")
	if err != nil {
		t.Fatal(err)
	}
	if receipts[0]["ReleasedGeneration"] != uint64(2) {
		t.Fatalf("released generation = %v, want 2", receipts[0]["ReleasedGeneration"])
	}
	wantDigest := assignmentPolicyExactEmptyDigest(target, "ps-sales", 2)
	if !bytes.Equal(assignmentPolicyBytes(receipts[0]["ExactEmptyDigest"]), wantDigest) {
		t.Fatalf("release receipt digest = %x, want %x", receipts[0]["ExactEmptyDigest"], wantDigest)
	}
	if rows := store.rows["PermissionSetAssignment"]; len(rows) != 0 {
		t.Fatalf("claim release retained policy-owned assignment tombstones: %#v", rows)
	}
	tombstones := store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable]
	if len(tombstones) != 0 {
		t.Fatalf("claim release retained finalized tombstone receipts: %#v", tombstones)
	}
}

func TestAssignmentPolicyLifecycleReleaseFinalizesOnlyExactPolicyOwnedTombstones(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, item, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	store.rows["PermissionSetAssignment"] = append(store.rows["PermissionSetAssignment"], cp.ComponentRow{
		"Id": "unrelated-tombstone", "PermissionSetId": "ps-sales",
		"AssigneeId": "user-unrelated", "IsDeleted": true,
		"LastModifiedDate": "2026-08-12T09:00:00Z",
	})
	store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable] = append(
		store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable],
		cp.ComponentRow{
			"Id": "unrelated-receipt", "AssignmentId": "unrelated-tombstone",
			"PermissionSetId": "ps-sales", "AssigneeId": "user-unrelated",
			"OwnerPack": "other-app", "PolicyKey": "other-policy",
			"TombstonedGeneration": uint64(7), "TombstonedAt": "2026-08-12T09:00:00Z",
		},
	)
	remove, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, remove, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, remove, removePlan, store,
	); err != nil {
		t.Fatal(err)
	}
	if row := assignmentPolicyTestAssignment(store, "user-sales"); row != nil {
		t.Fatalf("release retained its policy-owned assignment tombstone: %#v", row)
	}
	if row := assignmentPolicyTestAssignment(store, "user-unrelated"); row == nil || row["Id"] != "unrelated-tombstone" || row["IsDeleted"] != true {
		t.Fatalf("release changed unrelated tombstone history: %#v", row)
	}
	receipts := store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable]
	if len(receipts) != 1 || receipts[0]["Id"] != "unrelated-receipt" {
		t.Fatalf("release changed unrelated tombstone receipts: %#v", receipts)
	}
}

func TestAssignmentPolicyLifecycleRejectsMalformedDesiredKeyBeforeRelease(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_\x01users"})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	); err == nil || !strings.Contains(err.Error(), "policy key") {
		t.Fatalf("malformed desired policy key error = %v", err)
	}
	if claims := store.rows[schemacatalog.AssignmentPolicyClaimTable]; len(claims) != 1 ||
		claims[0]["PolicyKey"] != "sales_users" {
		t.Fatalf("malformed desired set changed claims: %#v", claims)
	}
	if row := assignmentPolicyTestAssignment(store, "user-sales"); row == nil || row["IsDeleted"] != false {
		t.Fatalf("malformed desired set changed assignments: %#v", row)
	}
}

func TestAssignmentPolicyLifecycleRejectsDesiredKeyCollationCollision(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	for name, keys := range map[string][]string{
		"case":   {"Sales_Users", "sales_users"},
		"accent": {"cafe_users", "café_users"},
	} {
		t.Run(name, func(t *testing.T) {
			request, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
				PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
			}, keys)
			if err != nil {
				t.Fatal(err)
			}
			if _, err := lifecycle.Plan(
				context.Background(), cp.ComponentManagedScopeSet, request, assignmentPolicyLifecycleStore(),
			); err == nil || !strings.Contains(err.Error(), "collide under database collation") {
				t.Fatalf("desired policy key collision error = %v", err)
			}
		})
	}
}

func TestAssignmentPolicyLifecycleExactSetRetainsDatabaseCollatedSource(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(
		assignmentPolicyManagedRequestFor(t, "app", "café_users", "Sales_User"),
	)
	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)
	}

	request, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, []string{"cafe_users"})
	if err != nil {
		t.Fatal(err)
	}
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, request, store)
	if err != nil {
		t.Fatal(err)
	}
	result, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, request, plan, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationNoChange {
		t.Fatalf("database-collated retained source disposition = %q", result.Disposition)
	}
	claims := store.rows[schemacatalog.AssignmentPolicyClaimTable]
	if len(claims) != 1 || claims[0]["PolicyKey"] != "café_users" {
		t.Fatalf("database-collated retained source was released: %#v", claims)
	}
	if row := assignmentPolicyTestAssignment(store, "user-sales"); row == nil || row["IsDeleted"] != false {
		t.Fatalf("database-collated retained source assignment changed: %#v", row)
	}
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeSet, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("database-collated exact-set projection differs:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
}

func TestAssignmentPolicyLifecycleSetProjectionUsesCaseInsensitiveSourceIdentity(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)
	}
	request, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, []string{"Sales_Users"})
	if err != nil {
		t.Fatal(err)
	}
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if result, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, request, plan, store,
	); err != nil {
		t.Fatal(err)
	} else if result.Disposition != cp.ComponentManagedMutationNoChange {
		t.Fatalf("case-only desired set disposition = %q", result.Disposition)
	}
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeSet, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("case-only set projection differs:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
}

func TestAssignmentPolicyLifecycleSetRemovalUsesCaseInsensitivePackIdentity(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)
	}
	request, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "App", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	if claims := store.rows[schemacatalog.AssignmentPolicyClaimTable]; len(claims) != 0 {
		t.Fatalf("case-only pack removal retained claims: %#v", claims)
	}
	if rows := store.rows["PermissionSetAssignment"]; len(rows) != 0 {
		t.Fatalf("case-only pack removal retained policy-owned tombstones: %#v", rows)
	}
	if rows := store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable]; len(rows) != 0 {
		t.Fatalf("case-only pack removal retained policy tombstone receipts: %#v", rows)
	}
}

func TestAssignmentPolicyLifecycleSetProjectionRejectsPostRemovalEffects(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,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, removePlan, store,
	); err != nil {
		t.Fatal(err)
	}

	// A transaction-bound effect runs after Mutate and before the host asks
	// Project for its staged proof. The set projection must still observe the
	// exact-empty release invariant, not only the absence of the claim row.
	store.rows["PermissionSetAssignment"] = append(store.rows["PermissionSetAssignment"], cp.ComponentRow{
		"Id": "post-release-assignment", "PermissionSetId": "ps-sales",
		"AssigneeId": "user-sales", "IsDeleted": false, "LastModifiedDate": "post-release",
	})
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err == nil && bytes.Equal(projection.Expected, projection.Live) {
		t.Fatal("set projection accepted a post-removal active assignment")
	}

	assignmentPolicyTestAssignment(store, "user-sales")["IsDeleted"] = true
	store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]["PhysicalKey"] = []byte("effect-drift")
	projection, err = lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err == nil && bytes.Equal(projection.Expected, projection.Live) {
		t.Fatal("set projection accepted post-removal target-state identity drift")
	}
}

func TestAssignmentPolicyLifecycleRemovalRejectsExhaustedGeneration(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)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	state["WorkGeneration"] = uint64(math.MaxUint64)
	state["AppliedGeneration"] = uint64(math.MaxUint64)
	setRequest, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, removePlan, store,
	); err == nil || !strings.Contains(err.Error(), "generation") {
		t.Fatalf("exhausted generation error = %v", err)
	}
}

func TestAssignmentPolicyLifecycleMutateRejectsEligibilityThatChangedAfterPlan(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	store.rows["User"] = append(store.rows["User"], cp.ComponentRow{
		"Id": "user-late", "ProfileId": "profile-sales", "IsActive": true, "IsDeleted": false,
	})
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err == nil || !strings.Contains(err.Error(), "stale") {
		t.Fatalf("post-plan eligibility drift error = %v", err)
	}
	if len(store.rows[schemacatalog.AssignmentPolicyClaimTable]) != 0 ||
		len(store.rows["PermissionSetAssignment"]) != 0 {
		t.Fatalf("stale plan mutated rows: claims=%#v assignments=%#v",
			store.rows[schemacatalog.AssignmentPolicyClaimTable], store.rows["PermissionSetAssignment"])
	}
}

func TestAssignmentPolicyLifecycleMutateDoesNotLockEligibilitySourceFacts(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	store.locks = nil
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	for _, query := range store.locks {
		if query.Table == "Profile" || query.Table == "User" {
			t.Fatalf("target-started lifecycle locked eligibility source %s: %#v", query.Table, query)
		}
	}
}

func TestAssignmentPolicyLifecycleItemEstimateBoundsReceiptBackedTransitions(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	for index := 0; index < 4; index++ {
		store.rows["PermissionSetAssignment"] = append(
			store.rows["PermissionSetAssignment"],
			cp.ComponentRow{
				"Id": fmt.Sprintf("manual-%d", index), "PermissionSetId": "ps-sales",
				"AssigneeId": fmt.Sprintf("user-old-%d", index), "IsDeleted": false,
			},
		)
	}
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	// Four active removals can each write a tombstone receipt and the PSA;
	// the desired assignee can be inserted, and the claim, release receipt,
	// selector, state creation, and final state update each contribute at most
	// one more mutation. The estimate must bound that transaction shape.
	if got := plan.Estimate().MutationStatements; got < 14 {
		t.Fatalf("item mutation statement estimate = %d, want at least 14", got)
	}
	if got := plan.Estimate().DeltaRows; got < 14 {
		t.Fatalf("item delta-row estimate = %d, want at least 14", got)
	}
}

func TestAssignmentPolicyLifecycleSetEstimateBoundsMaximumRelease(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)
	}
	for len(store.rows["PermissionSetAssignment"]) < assignmentPolicyMaximumRows {
		index := len(store.rows["PermissionSetAssignment"])
		store.rows["PermissionSetAssignment"] = append(
			store.rows["PermissionSetAssignment"],
			cp.ComponentRow{
				"Id": fmt.Sprintf("manual-%d", index), "PermissionSetId": "ps-sales",
				"AssigneeId": fmt.Sprintf("user-old-%d", index), "IsDeleted": false,
			},
		)
	}
	setRequest, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if got := plan.Estimate().MutationStatements; got < 2*assignmentPolicyMaximumRows+4 {
		t.Fatalf("set mutation statement estimate = %d, want at least %d", got, 2*assignmentPolicyMaximumRows+4)
	}
	if got := plan.Estimate().DeltaRows; got < 2*assignmentPolicyMaximumRows+4 {
		t.Fatalf("set delta-row estimate = %d, want at least %d", got, 2*assignmentPolicyMaximumRows+4)
	}
}

func TestAssignmentPolicyLifecycleSetEstimateExhaustsMoreThanOneAssignmentPage(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)
	}
	for len(store.rows["PermissionSetAssignment"]) < assignmentPolicyMaximumRows+1 {
		index := len(store.rows["PermissionSetAssignment"])
		store.rows["PermissionSetAssignment"] = append(
			store.rows["PermissionSetAssignment"],
			cp.ComponentRow{
				"Id": fmt.Sprintf("manual-%d", index), "PermissionSetId": "ps-sales",
				"AssigneeId": fmt.Sprintf("user-old-%d", index), "IsDeleted": false,
			},
		)
	}
	setRequest, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeSet, setRequest, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	want := 2*(assignmentPolicyMaximumRows+1) + 4
	if got := plan.Estimate().DeltaRows; got < want {
		t.Fatalf("multi-page set delta-row estimate = %d, want at least %d", got, want)
	}
	if got := plan.Estimate().MutationStatements; got < want {
		t.Fatalf("multi-page set statement estimate = %d, want at least %d", got, want)
	}
}

func TestAssignmentPolicyLifecycleRefusesTombstoneWithoutPolicyReceipt(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	store.rows["PermissionSetAssignment"] = []cp.ComponentRow{{
		"Id": "recycle-bin-owned", "PermissionSetId": "ps-sales",
		"AssigneeId": "user-sales", "IsDeleted": true,
	}}
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err == nil || !strings.Contains(err.Error(), "tombstone receipt") {
		t.Fatalf("unproven tombstone reactivation error = %v", err)
	}
}

func TestAssignmentPolicyLifecycleReapplyConsumesReleaseReceiptAndPreservesSuspension(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	item := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(context.Background(), cp.ComponentManagedScopeItem, item, plan, store); err != nil {
		t.Fatal(err)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	stateID := state["Id"]
	state["Suspended"] = true
	state["SuspensionActor"] = "operator-1"
	state["SuspensionReason"] = "incident"
	state["SuspendedAt"] = "2026-08-12T10:00:00Z"
	setRequest, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, setRequest, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(context.Background(), cp.ComponentManagedScopeSet, setRequest, removePlan, store); err != nil {
		t.Fatal(err)
	}
	if len(store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable]) != 1 {
		t.Fatalf("release receipts after removal = %#v", store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable])
	}

	reclaimPlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, item, store)
	if err != nil {
		t.Fatal(err)
	}
	result, err := lifecycle.Mutate(context.Background(), cp.ComponentManagedScopeItem, item, reclaimPlan, store)
	if err != nil {
		t.Fatal(err)
	}
	if result.Disposition != cp.ComponentManagedMutationSafeProjectionStaged {
		t.Fatalf("reclaim disposition = %q", result.Disposition)
	}
	if rows := store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable]; len(rows) != 0 {
		t.Fatalf("active claim retained stale release receipt: %#v", rows)
	}
	states := store.rows[schemacatalog.AssignmentPolicyTargetStateTable]
	if len(states) != 1 || states[0]["Id"] != stateID || states[0]["Suspended"] != true ||
		states[0]["SuspensionActor"] != "operator-1" || states[0]["SuspensionReason"] != "incident" ||
		states[0]["SuspendedAt"] != "2026-08-12T10:00:00Z" {
		t.Fatalf("reclaim target state = %#v", states)
	}
	if row := assignmentPolicyTestAssignment(store, "user-sales"); row != nil {
		t.Fatalf("suspended reapply created an assignment: %#v", row)
	}
}

func TestAssignmentPolicyLifecycleSameSourceReapplyValidatesMatchingReleaseEvidence(t *testing.T) {
	tests := []struct {
		name    string
		corrupt func(*permissionSetDefinitionLifecycleMemoryStore)
		want    string
	}{
		{
			name: "different target domain",
			corrupt: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable][0]["TargetDomain"] = "other/domain"
			},
			want: "target identity",
		},
		{
			name: "different physical identity",
			corrupt: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				receipt := store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable][0]
				target := cp.ComponentManagedTarget{
					TargetDomain:     permissionSetDefinitionManagedTargetDomain,
					PhysicalKeyBytes: []byte("different_target"),
				}
				receipt["PhysicalKey"] = append([]byte(nil), target.PhysicalKeyBytes...)
				receipt["ExactEmptyDigest"] = assignmentPolicyExactEmptyDigest(target, "ps-sales", 2)
			},
			want: "target identity",
		},
		{
			name: "zero released generation",
			corrupt: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				receipt := store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable][0]
				target, err := PermissionSetDefinitionManagedTarget("Sales_User")
				if err != nil {
					t.Fatal(err)
				}
				receipt["ReleasedGeneration"] = uint64(0)
				receipt["ExactEmptyDigest"] = assignmentPolicyExactEmptyDigest(target, "ps-sales", 0)
			},
			want: "generation",
		},
		{
			name: "different released generation",
			corrupt: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				receipt := store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable][0]
				target, err := PermissionSetDefinitionManagedTarget("Sales_User")
				if err != nil {
					t.Fatal(err)
				}
				receipt["ReleasedGeneration"] = uint64(3)
				receipt["ExactEmptyDigest"] = assignmentPolicyExactEmptyDigest(target, "ps-sales", 3)
			},
			want: "not converged removed",
		},
		{
			name: "forged exact-empty digest",
			corrupt: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable][0]["ExactEmptyDigest"] = []byte("forged")
			},
			want: "exact-empty digest",
		},
		{
			name: "target no longer exact empty",
			corrupt: func(store *permissionSetDefinitionLifecycleMemoryStore) {
				store.rows["PermissionSetAssignment"] = append(
					store.rows["PermissionSetAssignment"],
					cp.ComponentRow{
						"Id": "manual-active", "PermissionSetId": "ps-sales",
						"AssigneeId": "user-other", "IsDeleted": false,
					},
				)
			},
			want: "not exact-empty",
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			store, _ := assignmentPolicyReleasedTargetReplacementFixture(t)
			test.corrupt(store)
			lifecycle := assignmentPolicyTestLifecycleProvider()
			reclaim := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
			plan, err := lifecycle.Plan(
				context.Background(), cp.ComponentManagedScopeItem, reclaim, store,
			)
			if err != nil {
				t.Fatal(err)
			}
			if _, err := lifecycle.Mutate(
				context.Background(), cp.ComponentManagedScopeItem, reclaim, plan, store,
			); err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("same-source reclaim proof error = %v, want %q", err, test.want)
			}
			if row := assignmentPolicyTestAssignment(store, "user-sales"); row != nil {
				t.Fatalf("invalid same-source release evidence created assignment: %#v", row)
			}
		})
	}
}

func TestAssignmentPolicyLifecycleReplacementCreatesNewIdentitiesAfterRelease(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	store.rows["User"] = append(store.rows["User"], cp.ComponentRow{
		"Id": "user-sales-2", "ProfileId": "profile-sales", "IsActive": true, "IsDeleted": false,
	})
	first := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	firstPlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, first, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, first, firstPlan, store,
	); err != nil {
		t.Fatal(err)
	}
	originalIDs := map[string]any{}
	for _, assigneeID := range []string{"user-sales", "user-sales-2"} {
		originalIDs[assigneeID] = assignmentPolicyTestAssignment(store, assigneeID)["Id"]
	}
	remove, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, remove, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, remove, removePlan, store,
	); err != nil {
		t.Fatal(err)
	}
	replacement := cp.NewComponentManagedItemLifecycleRequest(
		assignmentPolicyManagedRequestFor(t, "other-app", "support_users", "Sales_User"),
	)
	replacementPlan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, replacement, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, replacement, replacementPlan, store,
	); err != nil {
		t.Fatal(err)
	}
	for _, assigneeID := range []string{"user-sales", "user-sales-2"} {
		row := assignmentPolicyTestAssignment(store, assigneeID)
		if row == nil || row["IsDeleted"] != false || row["Id"] == originalIDs[assigneeID] {
			t.Fatalf("replacement policy did not create a new identity for %s: %#v", assigneeID, row)
		}
	}
}

func TestAssignmentPolicyLifecycleNewPolicyCreatesNewIdentityForReleasedTarget(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	first := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	firstPlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, first, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, first, firstPlan, store,
	); err != nil {
		t.Fatal(err)
	}
	originalAssignmentID := assignmentPolicyTestAssignment(store, "user-sales")["Id"]
	remove, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, remove, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, remove, removePlan, store,
	); err != nil {
		t.Fatal(err)
	}

	secondRequest := assignmentPolicyManagedRequest(t)
	secondRequest.Component.BusinessKey = "support_users"
	policy, err := DecodePermissionSetAssignmentPolicy(secondRequest.Payload)
	if err != nil {
		t.Fatal(err)
	}
	secondRequest.ExpectedProjection, err = json.Marshal(assignmentPolicyDeclarationProjection{
		Present: true, PolicyKey: "support_users", OwnerPack: "app",
		PermissionSet: policy.PermissionSet, ProfilesAnyOf: policy.ProfilesAnyOf,
	})
	if err != nil {
		t.Fatal(err)
	}
	second := cp.NewComponentManagedItemLifecycleRequest(secondRequest)
	secondPlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, second, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, second, secondPlan, store,
	); err != nil {
		t.Fatal(err)
	}
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeItem, second, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("replacement policy projection differs:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
	if row := assignmentPolicyTestAssignment(store, "user-sales"); row == nil ||
		row["IsDeleted"] != false || row["Id"] == originalAssignmentID {
		t.Fatalf("replacement policy did not create a new released-target assignment: %#v", row)
	}
	if receipts := store.rows[schemacatalog.AssignmentPolicyTombstoneReceiptTable]; len(receipts) != 0 {
		t.Fatalf("replacement policy retained consumed tombstone receipt: %#v", receipts)
	}

	secondRemovePlan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeSet, remove, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, remove, secondRemovePlan, store,
	); err != nil {
		t.Fatal(err)
	}
	setProjection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeSet, remove, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(setProjection.Expected, setProjection.Live) {
		t.Fatalf("repeated release set projection differs:\nexpected %s\nlive %s", setProjection.Expected, setProjection.Live)
	}
	if receipts := store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable]; len(receipts) != 2 {
		t.Fatalf("repeated release receipts = %#v, want historical and current evidence", receipts)
	}
}

func TestAssignmentPolicyLifecycleEmptySetProjectionIsCanonical(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	request, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	projection, err := lifecycle.Project(
		context.Background(), cp.ComponentManagedScopeSet, request, assignmentPolicyLifecycleStore(),
	)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("empty set projection differs:\nexpected %s\nlive %s", projection.Expected, projection.Live)
	}
}

func TestAssignmentPolicyLifecycleRejectsAppliedGenerationAheadOfWork(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	state["WorkGeneration"] = uint64(1)
	state["AppliedGeneration"] = uint64(2)
	if _, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	); err == nil || !strings.Contains(err.Error(), "ahead") {
		t.Fatalf("invalid generation ordering error = %v", err)
	}
}

func TestAssignmentPolicyLifecycleRejectsNonBooleanSecurityState(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	store.rows["PermissionSet"][0]["IsActive"] = int64(2)
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	if _, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	); err == nil || !strings.Contains(err.Error(), "boolean") {
		t.Fatalf("invalid security boolean error = %v", err)
	}
}

func TestAssignmentPolicyLifecycleRejectsTargetStateIdentityDrift(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	state["TargetDomain"] = "wrong-domain"
	if _, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	); err == nil || !strings.Contains(err.Error(), "identity") {
		t.Fatalf("target-state identity error = %v", err)
	}
}

func TestAssignmentPolicyLifecycleProjectionAllowsOnlyMissingGrantsWhileGenerationPending(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(context.Background(), cp.ComponentManagedScopeItem, request, plan, store); err != nil {
		t.Fatal(err)
	}
	store.rows["User"] = append(store.rows["User"], cp.ComponentRow{
		"Id": "user-gain", "ProfileId": "profile-sales", "IsActive": true, "IsDeleted": false,
	})
	state := store.rows[schemacatalog.AssignmentPolicyTargetStateTable][0]
	state["WorkGeneration"] = uint64(2)
	state["AppliedGeneration"] = uint64(1)
	projection, err := lifecycle.Project(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("pending missing grant projection differs:\nexpected=%s\nlive=%s", projection.Expected, projection.Live)
	}

	assignmentPolicyTestAssignment(store, "user-sales")["IsDeleted"] = true
	projection, err = lifecycle.Project(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(projection.Expected, projection.Live) {
		t.Fatalf("pending all-missing grant projection differs:\nexpected=%s\nlive=%s", projection.Expected, projection.Live)
	}

	store.rows["PermissionSetAssignment"] = append(store.rows["PermissionSetAssignment"], cp.ComponentRow{
		"Id": "unsafe-extra", "PermissionSetId": "ps-sales", "AssigneeId": "user-old", "IsDeleted": false,
	})
	projection, err = lifecycle.Project(context.Background(), cp.ComponentManagedScopeItem, request, store)
	if err != nil {
		t.Fatal(err)
	}
	if bytes.Equal(projection.Expected, projection.Live) {
		t.Fatal("pending generation accepted an active assignment outside the eligible set")
	}
}

func TestAssignmentPolicyRetargetLocksPhysicalRootsInCanonicalIDOrder(t *testing.T) {
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	store.rows["PermissionSet"] = append(store.rows["PermissionSet"], cp.ComponentRow{
		"Id": "ps-aaa", "Name": "Previous_Target", "OwnerPack": "definitions",
		"IsActive": true, "IsDeleted": false, "IsCustom": true,
		"ProfileId": nil, "IsOwnedByProfile": false,
	})
	store.rows[schemacatalog.AssignmentPolicyClaimTable] = []cp.ComponentRow{{
		"Id": "claim-old", "PermissionSetId": "ps-aaa", "OwnerPack": "app",
		"PolicyKey": "sales_users", "PayloadHash": []byte("old"),
	}}
	store.rows[schemacatalog.AssignmentPolicyTargetStateTable] = []cp.ComponentRow{{
		"Id": "state-old", "TargetDomain": permissionSetDefinitionManagedTargetDomain,
		"PhysicalKey": []byte("previous_target"), "PermissionSetId": "ps-aaa",
		"Suspended": false, "SuspensionActor": nil, "SuspensionReason": nil,
		"SuspendedAt": nil, "WorkGeneration": uint64(4), "AppliedGeneration": uint64(4),
		"NextAttemptAt": nil, "RetryCount": uint64(0), "LastFailureCode": nil,
		"LastFailureDigest": nil, "ResolutionState": assignmentPolicyResolutionResolved,
		"ResolutionDigest": assignmentPolicyDigest(assignmentPolicyResolutionResolved),
	}}
	store.rows["PermissionSetAssignment"] = []cp.ComponentRow{{
		"Id": "assignment-old", "PermissionSetId": "ps-aaa",
		"AssigneeId": "user-sales", "IsDeleted": false,
	}}
	request := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	plan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, request, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	store.locks = nil
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, request, plan, store,
	); err != nil {
		t.Fatal(err)
	}
	var rootIDs []string
	for _, query := range store.locks {
		switch query.Table {
		case "PermissionSet":
			rootIDs = append(rootIDs, query.Equal["Id"].(string))
			if got := strings.Join(query.LogicalKeyColumns, ","); got != "Id" {
				t.Fatalf("physical root lock logical key = %s, want Id", got)
			}
		case schemacatalog.AssignmentPolicySelectorTable:
			if got := strings.Join(query.LogicalKeyColumns, ","); got != "PermissionSetId,ProfileName" {
				t.Fatalf("selector lock logical key = %s", got)
			}
		case "PermissionSetAssignment":
			if got := strings.Join(query.LogicalKeyColumns, ","); got != "PermissionSetId,AssigneeId" && got != "Id" {
				t.Fatalf("assignment lock logical key = %s", got)
			}
		}
	}
	if got := strings.Join(rootIDs, ","); got != "ps-aaa,ps-sales" {
		t.Fatalf("physical root lock order = %s", got)
	}
	receipts := store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable]
	if len(receipts) != 1 || receipts[0]["PermissionSetId"] != "ps-aaa" {
		t.Fatalf("retarget did not retain old-target release receipt: %#v", receipts)
	}

	remove, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, remove, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, remove, removePlan, store,
	); err != nil {
		t.Fatal(err)
	}
	receipts = store.rows[schemacatalog.AssignmentPolicyReleaseReceiptTable]
	if len(receipts) != 2 ||
		assignmentPolicyTestReleaseReceiptForTarget(receipts, "ps-aaa") == nil ||
		assignmentPolicyTestReleaseReceiptForTarget(receipts, "ps-sales") == nil {
		t.Fatalf("later release did not retain one receipt per released target: %#v", receipts)
	}

	replacement := cp.NewComponentManagedItemLifecycleRequest(
		assignmentPolicyManagedRequestFor(t, "other-app", "support_users", "Previous_Target"),
	)
	replacementPlan, err := lifecycle.Plan(
		context.Background(), cp.ComponentManagedScopeItem, replacement, store,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, replacement, replacementPlan, store,
	); err != nil {
		t.Fatal(err)
	}
	oldAssignment := assignmentPolicyTestAssignmentForTarget(store, "ps-aaa", "user-sales")
	if oldAssignment == nil || oldAssignment["Id"] == "assignment-old" || oldAssignment["IsDeleted"] != false {
		t.Fatalf("replacement policy did not create a new old-target assignment identity: %#v", oldAssignment)
	}
}

func assignmentPolicyLifecycleStore() *permissionSetDefinitionLifecycleMemoryStore {
	store := newPermissionSetDefinitionLifecycleMemoryStore()
	store.rows["PermissionSet"] = []cp.ComponentRow{{
		"Id": "ps-sales", "Name": "Sales_User", "OwnerPack": "definitions",
		"IsActive": true, "IsDeleted": false, "IsCustom": true,
		"ProfileId": nil, "IsOwnedByProfile": false,
	}}
	store.rows["Profile"] = []cp.ComponentRow{{
		"Id": "profile-sales", "Name": "Sales", "IsActive": true, "IsDeleted": false,
	}}
	store.rows["User"] = []cp.ComponentRow{{
		"Id": "user-sales", "ProfileId": "profile-sales", "IsActive": true, "IsDeleted": false,
	}}
	return store
}

func assignmentPolicyManagedRequest(t *testing.T) cp.ComponentManagedRequest {
	return assignmentPolicyManagedRequestFor(t, "app", "sales_users", "Sales_User")
}

func assignmentPolicyManagedRequestFor(
	t *testing.T,
	ownerPack, policyKey, permissionSet string,
) cp.ComponentManagedRequest {
	t.Helper()
	payload, err := json.Marshal(PermissionSetAssignmentPolicy{
		Schema: PermissionSetAssignmentPolicySchema, PermissionSet: permissionSet,
		ProfilesAnyOf: []string{"Sales"},
	})
	if err != nil {
		t.Fatal(err)
	}
	policy, err := DecodePermissionSetAssignmentPolicy(payload)
	if err != nil {
		t.Fatal(err)
	}
	expected, err := json.Marshal(assignmentPolicyDeclarationProjection{
		Present: true, PolicyKey: policyKey, OwnerPack: ownerPack,
		PermissionSet: policy.PermissionSet, ProfilesAnyOf: policy.ProfilesAnyOf,
	})
	if err != nil {
		t.Fatal(err)
	}
	return cp.ComponentManagedRequest{
		PackName: ownerPack,
		Component: cp.ManifestComponent{
			Kind: PermissionSetAssignmentPolicyComponentKind, BusinessKey: policyKey,
			Manageability: cp.ManageabilityLocked,
		},
		Payload: payload, ExpectedProjection: expected,
	}
}

func assignmentPolicyReleasedTargetReplacementFixture(
	t *testing.T,
) (*permissionSetDefinitionLifecycleMemoryStore, cp.ComponentManagedLifecycleRequest) {
	t.Helper()
	lifecycle := assignmentPolicyTestLifecycleProvider()
	store := assignmentPolicyLifecycleStore()
	first := cp.NewComponentManagedItemLifecycleRequest(assignmentPolicyManagedRequest(t))
	firstPlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeItem, first, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeItem, first, firstPlan, store,
	); err != nil {
		t.Fatal(err)
	}
	remove, err := cp.NewComponentManagedSetLifecycleRequest(cp.ComponentManagedSetRequest{
		PackName: "app", Kind: PermissionSetAssignmentPolicyComponentKind,
	}, nil)
	if err != nil {
		t.Fatal(err)
	}
	removePlan, err := lifecycle.Plan(context.Background(), cp.ComponentManagedScopeSet, remove, store)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := lifecycle.Mutate(
		context.Background(), cp.ComponentManagedScopeSet, remove, removePlan, store,
	); err != nil {
		t.Fatal(err)
	}
	replacement := cp.NewComponentManagedItemLifecycleRequest(
		assignmentPolicyManagedRequestFor(t, "other-app", "support_users", "Sales_User"),
	)
	return store, replacement
}

func assignmentPolicyTestReleaseReceiptForTarget(rows []cp.ComponentRow, permissionSetID string) cp.ComponentRow {
	for _, row := range rows {
		if row["PermissionSetId"] == permissionSetID {
			return row
		}
	}
	return nil
}

func assignmentPolicyTestAssignment(store *permissionSetDefinitionLifecycleMemoryStore, assignee string) cp.ComponentRow {
	for _, row := range store.rows["PermissionSetAssignment"] {
		if row["AssigneeId"] == assignee {
			return row
		}
	}
	return nil
}

func assignmentPolicyTestAssignmentForTarget(
	store *permissionSetDefinitionLifecycleMemoryStore,
	permissionSetID, assignee string,
) cp.ComponentRow {
	for _, row := range store.rows["PermissionSetAssignment"] {
		if row["PermissionSetId"] == permissionSetID && row["AssigneeId"] == assignee {
			return row
		}
	}
	return nil
}

func assertAssignmentPolicySourceTarget(t *testing.T, plan cp.ComponentManagedPlan, ownerPack, policyKey string) {
	t.Helper()
	source, err := NewPermissionSetAssignmentPolicySource(ownerPack, policyKey)
	if err != nil {
		t.Fatal(err)
	}
	assertAssignmentPolicyManagedTarget(t, plan, source.ManagedTarget())
}

func assertAssignmentPolicyManagedTarget(
	t *testing.T,
	plan cp.ComponentManagedPlan,
	want cp.ComponentManagedTarget,
) {
	t.Helper()
	for _, target := range plan.Targets() {
		if target.TargetDomain == want.TargetDomain &&
			bytes.Equal(target.PhysicalKeyBytes, want.PhysicalKeyBytes) {
			return
		}
	}
	t.Fatalf("managed plan targets = %#v, want source target %#v", plan.Targets(), want)
}

func (s *permissionSetDefinitionLifecycleMemoryStore) ReadRowsPage(
	ctx context.Context,
	page cp.ComponentManagedKeysetRowQuery,
) ([]cp.ComponentRow, error) {
	result, err := s.ReadRowsKeysetPage(ctx, page)
	return result.Rows, err
}

func (s *permissionSetDefinitionLifecycleMemoryStore) ReadRowsKeysetPage(
	ctx context.Context,
	page cp.ComponentManagedKeysetRowQuery,
) (cp.ComponentManagedKeysetRowPage, error) {
	if err := page.Validate(permissionSetDefinitionManagedMaximumRows); err != nil {
		return cp.ComponentManagedKeysetRowPage{}, err
	}
	s.reads = append(s.reads, page.Query)
	rows := assignmentPolicyTestRows(s, page.Query)
	return assignmentPolicyTestKeysetPage(rows, page), nil
}

func (s *permissionSetDefinitionLifecycleMemoryStore) LockRowsPage(
	ctx context.Context,
	page cp.ComponentManagedKeysetRowQuery,
) ([]cp.ComponentRow, error) {
	result, err := s.LockRowsKeysetPage(ctx, page)
	return result.Rows, err
}

func (s *permissionSetDefinitionLifecycleMemoryStore) LockRowsKeysetPage(
	ctx context.Context,
	page cp.ComponentManagedKeysetRowQuery,
) (cp.ComponentManagedKeysetRowPage, error) {
	if err := page.Validate(permissionSetDefinitionManagedMaximumRows); err != nil {
		return cp.ComponentManagedKeysetRowPage{}, err
	}
	s.locks = append(s.locks, page.Query)
	rows := assignmentPolicyTestRows(s, page.Query)
	return assignmentPolicyTestKeysetPage(rows, page), nil
}

func assignmentPolicyTestRows(
	store *permissionSetDefinitionLifecycleMemoryStore,
	query cp.ComponentManagedRowQuery,
) []cp.ComponentRow {
	rows := make([]cp.ComponentRow, 0, len(store.rows[query.Table]))
	for _, source := range store.rows[query.Table] {
		if !assignmentPolicyTestRowMatches(query.Table, source, query.Equal) {
			continue
		}
		row := make(cp.ComponentRow, len(query.Columns))
		for _, column := range query.Columns {
			if value, ok := source[column]; ok {
				row[column] = value
			}
		}
		rows = append(rows, row)
	}
	sort.Slice(rows, func(i, j int) bool {
		for _, order := range query.OrderBy {
			left, right := fmt.Sprint(rows[i][order.Column]), fmt.Sprint(rows[j][order.Column])
			if left != right {
				if order.Direction == cp.ComponentManagedOrderDescending {
					return left > right
				}
				return left < right
			}
		}
		return false
	})
	return rows
}

func assignmentPolicyTestRowMatches(table string, row, equal cp.ComponentRow) bool {
	for column, expected := range equal {
		if (table == schemacatalog.AssignmentPolicyClaimTable ||
			table == schemacatalog.AssignmentPolicyReleaseReceiptTable ||
			table == schemacatalog.AssignmentPolicyTombstoneReceiptTable) &&
			(column == "OwnerPack" || column == "PolicyKey") {
			actual, actualOK := row[column].(string)
			wanted, wantedOK := expected.(string)
			if !actualOK || !wantedOK || !assignmentPolicyDatabaseIdentityEqual(actual, wanted) {
				return false
			}
			continue
		}
		if !permissionSetDefinitionLifecycleRowMatches(row, cp.ComponentRow{column: expected}) {
			return false
		}
	}
	return true
}

func assignmentPolicyTestKeysetPage(
	rows []cp.ComponentRow,
	page cp.ComponentManagedKeysetRowQuery,
) cp.ComponentManagedKeysetRowPage {
	filtered := make([]cp.ComponentRow, 0, len(rows))
	for _, row := range rows {
		if assignmentPolicyTestRowAfter(row, page.Query.OrderBy, page.ContinueAfter) {
			filtered = append(filtered, row)
		}
	}
	exhausted := len(filtered) <= page.Query.MaximumRows
	if !exhausted {
		filtered = filtered[:page.Query.MaximumRows]
	}
	return cp.ComponentManagedKeysetRowPage{Rows: filtered, Exhausted: exhausted}
}

func assignmentPolicyTestRowAfter(
	row cp.ComponentRow,
	order []cp.ComponentManagedOrder,
	after cp.ComponentRow,
) bool {
	if len(after) == 0 {
		return true
	}
	for _, item := range order {
		comparison := strings.Compare(fmt.Sprint(row[item.Column]), fmt.Sprint(after[item.Column]))
		if comparison == 0 {
			continue
		}
		if item.Direction == cp.ComponentManagedOrderDescending {
			return comparison < 0
		}
		return comparison > 0
	}
	return false
}
