package controlplane

import (
	"bytes"
	"context"
	"errors"
	"strings"
	"testing"
	"time"
)

func TestComponentManagedNoTargetsIsStableSentinel(t *testing.T) {
	if ErrComponentManagedNoTargets == nil ||
		!errors.Is(ErrComponentManagedNoTargets, ErrComponentManagedNoTargets) {
		t.Fatal("managed no-target result is not a stable error sentinel")
	}
}

func TestComponentManagedScopeIsClosedAndIncludesPlannedTransaction(t *testing.T) {
	tests := []struct {
		name  string
		scope ComponentManagedScope
	}{
		{name: "item", scope: ComponentManagedScopeItem},
		{name: "set", scope: ComponentManagedScopeSet},
		{name: "planned transaction", scope: ComponentManagedScopePlannedTransaction},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			if err := test.scope.Validate(); err != nil {
				t.Fatalf("Validate() error = %v", err)
			}
		})
	}
	if got := string(ComponentManagedScopePlannedTransaction); got != "planned_tx" {
		t.Fatalf("planned transaction scope = %q, want %q", got, "planned_tx")
	}
	if err := ComponentManagedScope("ambient").Validate(); err == nil || !strings.Contains(err.Error(), "unsupported") {
		t.Fatalf("unknown non-empty scope validation error = %v", err)
	}
	if err := (ComponentManagedLifecycleRequest{}).Validate(ComponentManagedScopePlannedTransaction); err == nil ||
		!strings.Contains(err.Error(), "lifecycle scope") {
		t.Fatalf("planned transaction lifecycle request validation error = %v", err)
	}
}

type legacyManagedTransactionReadStore struct {
	ComponentManagedReadStore
}

func (legacyManagedTransactionReadStore) ReadRowsPage(context.Context, ComponentManagedKeysetRowQuery) ([]ComponentRow, error) {
	return nil, nil
}

type legacyManagedTransactionMutationStore struct {
	ComponentManagedMutationStore
}

func (legacyManagedTransactionMutationStore) ReadRowsPage(context.Context, ComponentManagedKeysetRowQuery) ([]ComponentRow, error) {
	return nil, nil
}

func (legacyManagedTransactionMutationStore) LockRowsPage(context.Context, ComponentManagedKeysetRowQuery) ([]ComponentRow, error) {
	return nil, nil
}

var (
	_ ComponentManagedTransactionReadStore     = legacyManagedTransactionReadStore{}
	_ ComponentManagedTransactionMutationStore = legacyManagedTransactionMutationStore{}
)

func TestComponentManagedPlanOwnsCanonicalImmutableSnapshot(t *testing.T) {
	targets := []ComponentManagedTarget{
		{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte{'b', 0, '2'}},
		{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte{'a', 0, '1'}},
		{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte{'a', 0, '1'}},
	}
	opaque := []byte(`{"stale":["closed"]}`)
	identity := []byte("workflow/acme")
	plan, err := NewComponentManagedPlan(ComponentManagedPlanSpec{
		Targets: targets,
		RequiredCapabilities: []ComponentManagedCapability{
			ComponentManagedCapabilityExactSet,
			ComponentManagedCapabilityTargetSerialization,
		},
		Estimate:           ComponentManagedUsage{CurrentItems: 2, DesiredItems: 1, DeltaRows: 1, MutationStatements: 1, MutationBytes: 64},
		ProjectionIdentity: identity,
		OpaquePlan:         opaque,
	})
	if err != nil {
		t.Fatal(err)
	}

	targets[0].PhysicalKeyBytes[0] = 'z'
	opaque[0] = '!'
	identity[0] = '!'
	gotTargets := plan.Targets()
	if len(gotTargets) != 2 || string(gotTargets[0].PhysicalKeyBytes) != "a\x001" || string(gotTargets[1].PhysicalKeyBytes) != "b\x002" {
		t.Fatalf("Targets() = %#v", gotTargets)
	}
	gotTargets[0].PhysicalKeyBytes[0] = 'x'
	if string(plan.Targets()[0].PhysicalKeyBytes) != "a\x001" {
		t.Fatal("target bytes changed through accessor mutation")
	}
	if string(plan.OpaquePlan()) != `{"stale":["closed"]}` || string(plan.ProjectionIdentity()) != "workflow/acme" {
		t.Fatal("plan bytes changed through input mutation")
	}
	if len(plan.Fingerprint()) != 32 || bytes.Equal(plan.Fingerprint(), make([]byte, 32)) {
		t.Fatalf("Fingerprint() = %x", plan.Fingerprint())
	}
}

func TestComponentManagedPlanFingerprintIncludesWholeEnvelope(t *testing.T) {
	baseSpec := ComponentManagedPlanSpec{
		Targets:            []ComponentManagedTarget{{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte("open")}},
		ProjectionIdentity: []byte("workflow/acme"),
		OpaquePlan:         []byte(`{}`),
	}
	base := mustComponentManagedPlanFromSpec(t, baseSpec)
	tests := []struct {
		name string
		edit func(*ComponentManagedUsage)
	}{
		{name: "current items", edit: func(v *ComponentManagedUsage) { v.CurrentItems = 1 }},
		{name: "desired items", edit: func(v *ComponentManagedUsage) { v.DesiredItems = 1 }},
		{name: "delta rows", edit: func(v *ComponentManagedUsage) { v.DeltaRows = 1 }},
		{name: "rows read", edit: func(v *ComponentManagedUsage) { v.RowsRead = 1 }},
		{name: "rows locked", edit: func(v *ComponentManagedUsage) { v.RowsLocked = 1 }},
		{name: "rows inserted", edit: func(v *ComponentManagedUsage) { v.RowsInserted = 1 }},
		{name: "rows updated", edit: func(v *ComponentManagedUsage) { v.RowsUpdated = 1 }},
		{name: "rows deleted", edit: func(v *ComponentManagedUsage) { v.RowsDeleted = 1 }},
		{name: "mutation bytes", edit: func(v *ComponentManagedUsage) { v.MutationBytes = 1 }},
		{name: "mutation statements", edit: func(v *ComponentManagedUsage) { v.MutationStatements = 1 }},
		{name: "evidence writes", edit: func(v *ComponentManagedUsage) { v.EvidenceWrites = 1 }},
		{name: "effect calls", edit: func(v *ComponentManagedUsage) { v.EffectCalls = 1 }},
		{name: "batch rows", edit: func(v *ComponentManagedUsage) { v.BatchRows = 1 }},
		{name: "lock wait", edit: func(v *ComponentManagedUsage) { v.LockWait = time.Second }},
		{name: "elapsed", edit: func(v *ComponentManagedUsage) { v.Elapsed = time.Second }},
		{name: "attempts", edit: func(v *ComponentManagedUsage) { v.Attempts = 1 }},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			spec := baseSpec
			test.edit(&spec.Estimate)
			if base.SameEnvelope(mustComponentManagedPlanFromSpec(t, spec)) {
				t.Fatal("plans with different usage compare as the same envelope")
			}
		})
	}
}

func TestComponentManagedPlanFingerprintSeparatesVariableLengthSections(t *testing.T) {
	baseSpec := ComponentManagedPlanSpec{
		Targets: []ComponentManagedTarget{
			{TargetDomain: "a", PhysicalKeyBytes: []byte("root")},
			{TargetDomain: string(ComponentManagedCapabilityFreshProjection), PhysicalKeyBytes: []byte(ComponentManagedCapabilityStructuredMutation)},
		},
		Effects: []ComponentManagedEffectRequirement{
			{Class: "a", Triggers: []ComponentManagedMutationDisposition{ComponentManagedMutationDesiredProjectionStaged}},
			{Class: ComponentManagedEffectClass(ComponentManagedMutationSafeProjectionStaged), Triggers: []ComponentManagedMutationDisposition{ComponentManagedMutationDesiredProjectionStaged, ComponentManagedMutationSafeProjectionStaged}},
		},
		ProjectionIdentity: []byte("workflow/acme"),
		OpaquePlan:         []byte(`{}`),
	}
	base := mustComponentManagedPlanFromSpec(t, baseSpec)

	t.Run("targets and capabilities", func(t *testing.T) {
		boundarySpec := baseSpec
		boundarySpec.Targets = baseSpec.Targets[:1]
		boundarySpec.RequiredCapabilities = []ComponentManagedCapability{
			ComponentManagedCapabilityFreshProjection,
			ComponentManagedCapabilityStructuredMutation,
		}
		if base.SameEnvelope(mustComponentManagedPlanFromSpec(t, boundarySpec)) {
			t.Fatal("target and capability boundaries are ambiguous")
		}
	})

	t.Run("effect triggers", func(t *testing.T) {
		effectSpec := baseSpec
		effectSpec.Effects = []ComponentManagedEffectRequirement{
			{Class: "a", Triggers: []ComponentManagedMutationDisposition{ComponentManagedMutationDesiredProjectionStaged, ComponentManagedMutationSafeProjectionStaged}},
			{Class: ComponentManagedEffectClass(ComponentManagedMutationDesiredProjectionStaged), Triggers: []ComponentManagedMutationDisposition{ComponentManagedMutationSafeProjectionStaged}},
		}
		if base.SameEnvelope(mustComponentManagedPlanFromSpec(t, effectSpec)) {
			t.Fatal("effect trigger group boundaries are ambiguous")
		}
	})
}

func TestComponentManagedPlanRejectsInvalidEnvelope(t *testing.T) {
	valid := ComponentManagedPlanSpec{
		Targets:            []ComponentManagedTarget{{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte("open")}},
		ProjectionIdentity: []byte("workflow/acme"),
		OpaquePlan:         []byte(`{}`),
	}
	tests := []struct {
		name string
		edit func(*ComponentManagedPlanSpec)
		want string
	}{
		{name: "target domain", edit: func(v *ComponentManagedPlanSpec) { v.Targets[0].TargetDomain = " workflow/status" }, want: "target domain"},
		{name: "target key", edit: func(v *ComponentManagedPlanSpec) { v.Targets[0].PhysicalKeyBytes = nil }, want: "physical key"},
		{name: "projection identity", edit: func(v *ComponentManagedPlanSpec) { v.ProjectionIdentity = nil }, want: "projection identity"},
		{name: "opaque plan", edit: func(v *ComponentManagedPlanSpec) { v.OpaquePlan = nil }, want: "opaque plan"},
		{name: "opaque bound", edit: func(v *ComponentManagedPlanSpec) {
			v.OpaquePlan = make([]byte, ComponentManagedMaximumOpaquePlanBytes+1)
		}, want: "opaque plan"},
		{name: "negative estimate", edit: func(v *ComponentManagedPlanSpec) { v.Estimate.DeltaRows = -1 }, want: "estimate"},
		{name: "capability", edit: func(v *ComponentManagedPlanSpec) { v.RequiredCapabilities = []ComponentManagedCapability{"unknown"} }, want: "capability"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			value := valid
			value.Targets = append([]ComponentManagedTarget(nil), valid.Targets...)
			test.edit(&value)
			if _, err := NewComponentManagedPlan(value); err == nil || !strings.Contains(strings.ToLower(err.Error()), test.want) {
				t.Fatalf("NewComponentManagedPlan() error = %v, want containing %q", err, test.want)
			}
		})
	}
}

func TestComponentManagedLifecycleRegistrationIsAdditiveAndImmutable(t *testing.T) {
	provider := completeComponentManagedLifecycleProvider()
	readSet := []ComponentReadSurface{{Table: "WorkflowPolicy", Fact: "workflow policy"}}
	capabilities := []ComponentManagedCapability{
		ComponentManagedCapabilityExactSet,
		ComponentManagedCapabilityTargetSerialization,
		ComponentManagedCapabilityStructuredMutation,
		ComponentManagedCapabilityEvidence,
		ComponentManagedCapabilityFreshProjection,
	}
	registration, err := NewComponentManagedLifecycleRegistration(ComponentManagedLifecycleRegistrationSpec{
		Kind:            ComponentKindApprovalProcess,
		ReadSet:         readSet,
		Capabilities:    capabilities,
		TransactionMode: ComponentManagedTransactionPessimisticReadCommitted,
		Budget:          validComponentManagedBudget(),
		Lifecycle:       provider,
	})
	if err != nil {
		t.Fatal(err)
	}
	readSet[0].Table = "mutated"
	capabilities[0] = "mutated"
	if got := registration.ReadSet(); len(got) != 1 || got[0].Table != "WorkflowPolicy" {
		t.Fatalf("ReadSet() = %#v", got)
	}
	if got := registration.Capabilities(); len(got) != 5 || got[0] != ComponentManagedCapabilityExactSet || got[1] != ComponentManagedCapabilityFreshProjection || got[2] != ComponentManagedCapabilityEvidence || got[3] != ComponentManagedCapabilityStructuredMutation || got[4] != ComponentManagedCapabilityTargetSerialization {
		t.Fatalf("Capabilities() = %#v", got)
	}
	if registration.Kind() != ComponentKindApprovalProcess || registration.TransactionMode() != ComponentManagedTransactionPessimisticReadCommitted {
		t.Fatalf("registration identity = %q / %q", registration.Kind(), registration.TransactionMode())
	}
	legacyRegistration := ComponentKindRegistration{
		ComponentKindContract{Kind: ComponentKindApprovalProcess},
		SourceBackedComponentCapability{Kind: ComponentKindApprovalProcess},
		nil,
		nil,
		nil,
	}
	bound, err := legacyRegistration.WithManagedLifecycle(registration)
	if err != nil {
		t.Fatal(err)
	}
	if bound.Registration().Contract.Kind != ComponentKindApprovalProcess || bound.ManagedLifecycle().Kind() != ComponentKindApprovalProcess {
		t.Fatalf("bound registration = %#v / %#v", bound.Registration(), bound.ManagedLifecycle())
	}
	if _, err := legacyRegistration.WithManagedLifecycle(ComponentManagedLifecycleRegistration{}); err == nil || !strings.Contains(err.Error(), "required") {
		t.Fatalf("zero lifecycle binding error = %v", err)
	}
	mismatchedRegistration := legacyRegistration
	mismatchedRegistration.Contract.Kind = ComponentKindReportType
	if _, err := mismatchedRegistration.WithManagedLifecycle(registration); err == nil || !strings.Contains(err.Error(), "does not match") {
		t.Fatalf("mismatched lifecycle binding error = %v", err)
	}
	if err := registration.ValidatePlanCapabilities(mustComponentManagedPlan(t, []ComponentManagedCapability{ComponentManagedCapabilityExactSet})); err != nil {
		t.Fatalf("declared plan capability: %v", err)
	}
	if err := registration.ValidatePlanCapabilities(mustComponentManagedPlan(t, []ComponentManagedCapability{ComponentManagedCapabilityEffects})); err == nil || !strings.Contains(err.Error(), "undeclared") {
		t.Fatalf("undeclared plan capability error = %v", err)
	}
	if err := registration.ValidatePlanCapabilities(ComponentManagedPlan{}); err == nil || !strings.Contains(err.Error(), "targets") {
		t.Fatalf("zero plan error = %v", err)
	}
	overBudget := mustComponentManagedPlanFromSpec(t, ComponentManagedPlanSpec{
		Targets:            []ComponentManagedTarget{{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte("open")}},
		Estimate:           ComponentManagedUsage{CurrentItems: registration.Budget().MaxCurrentItems + 1},
		ProjectionIdentity: []byte("workflow/acme"),
		OpaquePlan:         []byte(`{}`),
	})
	if err := registration.ValidatePlanCapabilities(overBudget); err == nil || !strings.Contains(err.Error(), "current items") {
		t.Fatalf("over-budget plan error = %v", err)
	}
	targets := make([]ComponentManagedTarget, registration.Budget().MaxTargets+1)
	for index := range targets {
		targets[index] = ComponentManagedTarget{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte{byte(index >> 8), byte(index)}}
	}
	overTargetBudget := mustComponentManagedPlanFromSpec(t, ComponentManagedPlanSpec{
		Targets:            targets,
		ProjectionIdentity: []byte("workflow/acme"),
		OpaquePlan:         []byte(`{}`),
	})
	if err := registration.ValidatePlanCapabilities(overTargetBudget); err == nil || !strings.Contains(err.Error(), "targets") {
		t.Fatalf("over-target-budget plan error = %v", err)
	}
}

func TestComponentManagedLifecycleRegistrationRejectsIncompleteOrUnboundedInput(t *testing.T) {
	valid := ComponentManagedLifecycleRegistrationSpec{
		Kind:    ComponentKindApprovalProcess,
		ReadSet: []ComponentReadSurface{{Table: "ProcessDefinition", Fact: "approval definition"}},
		Capabilities: []ComponentManagedCapability{
			ComponentManagedCapabilityTargetSerialization,
			ComponentManagedCapabilityStructuredMutation,
			ComponentManagedCapabilityEvidence,
			ComponentManagedCapabilityFreshProjection,
		},
		TransactionMode: ComponentManagedTransactionPessimisticReadCommitted,
		Budget:          validComponentManagedBudget(),
		Lifecycle:       completeComponentManagedLifecycleProvider(),
	}
	tests := []struct {
		name string
		edit func(*ComponentManagedLifecycleRegistrationSpec)
		want string
	}{
		{name: "kind", edit: func(v *ComponentManagedLifecycleRegistrationSpec) { v.Kind = "" }, want: "kind"},
		{name: "read set", edit: func(v *ComponentManagedLifecycleRegistrationSpec) { v.ReadSet = nil }, want: "read set"},
		{name: "duplicate read", edit: func(v *ComponentManagedLifecycleRegistrationSpec) { v.ReadSet = append(v.ReadSet, v.ReadSet[0]) }, want: "more than once"},
		{name: "required capability", edit: func(v *ComponentManagedLifecycleRegistrationSpec) { v.Capabilities = v.Capabilities[:3] }, want: "required capability"},
		{name: "effect class capability", edit: func(v *ComponentManagedLifecycleRegistrationSpec) {
			v.EffectClasses = []ComponentManagedEffectClass{"fixture_effect"}
		}, want: "effect classes require"},
		{name: "effect capability class", edit: func(v *ComponentManagedLifecycleRegistrationSpec) {
			v.Capabilities = append(v.Capabilities, ComponentManagedCapabilityEffects)
		}, want: "requires at least one effect class"},
		{name: "mode", edit: func(v *ComponentManagedLifecycleRegistrationSpec) { v.TransactionMode = "" }, want: "transaction mode"},
		{name: "budget", edit: func(v *ComponentManagedLifecycleRegistrationSpec) { v.Budget.MaxBatchRows = 0 }, want: "budget"},
		{name: "provider", edit: func(v *ComponentManagedLifecycleRegistrationSpec) { v.Lifecycle.Mutate = nil }, want: "mutate"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			value := valid
			value.ReadSet = append([]ComponentReadSurface(nil), valid.ReadSet...)
			test.edit(&value)
			if _, err := NewComponentManagedLifecycleRegistration(value); err == nil || !strings.Contains(strings.ToLower(err.Error()), test.want) {
				t.Fatalf("NewComponentManagedLifecycleRegistration() error = %v, want containing %q", err, test.want)
			}
		})
	}
}

func TestComponentManagedLifecycleRegistrationValidatesHostMutationReceipt(t *testing.T) {
	registration, err := NewComponentManagedLifecycleRegistration(ComponentManagedLifecycleRegistrationSpec{
		Kind:            ComponentKindApprovalProcess,
		ReadSet:         []ComponentReadSurface{{Table: "ProcessDefinition", Fact: "approval definition"}},
		Capabilities:    []ComponentManagedCapability{ComponentManagedCapabilityTargetSerialization, ComponentManagedCapabilityStructuredMutation, ComponentManagedCapabilityEvidence, ComponentManagedCapabilityFreshProjection},
		TransactionMode: ComponentManagedTransactionPessimisticReadCommitted,
		Budget:          validComponentManagedBudget(),
		Lifecycle:       completeComponentManagedLifecycleProvider(),
	})
	if err != nil {
		t.Fatal(err)
	}
	plan := mustComponentManagedPlan(t, nil)
	valid := ComponentManagedUsage{
		CurrentItems: 2, RowsRead: 1, RowsLocked: 1,
		DeltaRows: 1, RowsInserted: 1, MutationBytes: 32, MutationStatements: 1,
		EvidenceWrites: 1, BatchRows: 1, LockWait: time.Millisecond, Elapsed: 2 * time.Millisecond, Attempts: 1,
	}
	if err := registration.ValidateMutationReceipt(plan, ComponentManagedMutationResult{Disposition: ComponentManagedMutationDesiredProjectionStaged}, ComponentManagedMutationReceipt{Usage: valid}); err != nil {
		t.Fatalf("valid mutation receipt: %v", err)
	}
	tests := []struct {
		name        string
		disposition ComponentManagedMutationDisposition
		edit        func(*ComponentManagedUsage)
		want        string
	}{
		{name: "current item counters", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.CurrentItems++ }, want: "current items"},
		{name: "delta counters", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.DeltaRows++ }, want: "delta rows"},
		{name: "delta budget", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) {
			v.DeltaRows = registration.Budget().MaxDeltaRows + 1
			v.RowsInserted = v.DeltaRows
		}, want: "delta rows"},
		{name: "byte budget", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.MutationBytes = registration.Budget().MaxMutationBytes + 1 }, want: "mutation bytes"},
		{name: "statement budget", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.MutationStatements = registration.Budget().MaxMutationStatements + 1 }, want: "mutation statements"},
		{name: "batch budget", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.BatchRows = registration.Budget().MaxBatchRows + 1 }, want: "batch rows"},
		{name: "lock wait budget", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.LockWait = registration.Budget().MaxLockWait + time.Nanosecond }, want: "lock wait"},
		{name: "elapsed budget", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.Elapsed = registration.Budget().MaxElapsed + time.Nanosecond }, want: "elapsed"},
		{name: "attempt budget", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.Attempts = registration.Budget().MaxAttempts + 1 }, want: "attempts"},
		{name: "evidence receipt", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.EvidenceWrites = 0 }, want: "evidence"},
		{name: "undeclared effect", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) { v.EffectCalls = 1 }, want: "effect calls"},
		{name: "no-change contradiction", disposition: ComponentManagedMutationNoChange, edit: func(*ComponentManagedUsage) {}, want: "no_change"},
		{name: "changed contradiction", disposition: ComponentManagedMutationDesiredProjectionStaged, edit: func(v *ComponentManagedUsage) {
			v.DeltaRows = 0
			v.RowsInserted = 0
			v.MutationBytes = 0
			v.MutationStatements = 0
		}, want: "requires"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			usage := valid
			test.edit(&usage)
			err := registration.ValidateMutationReceipt(plan, ComponentManagedMutationResult{Disposition: test.disposition}, ComponentManagedMutationReceipt{Usage: usage})
			if err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("ValidateMutationReceipt() error = %v, want containing %q", err, test.want)
			}
		})
	}
}

func TestComponentManagedLifecycleRegistrationValidatesExactEffectClasses(t *testing.T) {
	registration, err := NewComponentManagedLifecycleRegistration(ComponentManagedLifecycleRegistrationSpec{
		Kind:            ComponentKindApprovalProcess,
		ReadSet:         []ComponentReadSurface{{Table: "ProcessDefinition", Fact: "approval definition"}},
		Capabilities:    []ComponentManagedCapability{ComponentManagedCapabilityTargetSerialization, ComponentManagedCapabilityStructuredMutation, ComponentManagedCapabilityEffects, ComponentManagedCapabilityEvidence, ComponentManagedCapabilityFreshProjection},
		EffectClasses:   []ComponentManagedEffectClass{"effect_a", "effect_b"},
		TransactionMode: ComponentManagedTransactionPessimisticReadCommitted,
		Budget:          validComponentManagedBudget(),
		Lifecycle:       completeComponentManagedLifecycleProvider(),
	})
	if err != nil {
		t.Fatal(err)
	}
	plan := mustComponentManagedPlanFromSpec(t, ComponentManagedPlanSpec{
		Targets:              []ComponentManagedTarget{{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte("open")}},
		RequiredCapabilities: []ComponentManagedCapability{ComponentManagedCapabilityEffects},
		Effects: []ComponentManagedEffectRequirement{
			{Class: "effect_a", Triggers: []ComponentManagedMutationDisposition{ComponentManagedMutationDesiredProjectionStaged}},
			{Class: "effect_b", Triggers: []ComponentManagedMutationDisposition{ComponentManagedMutationDesiredProjectionStaged}},
		},
		ProjectionIdentity: []byte("workflow/acme"),
		OpaquePlan:         []byte(`{}`),
	})
	usage := ComponentManagedUsage{EffectCalls: 2, EvidenceWrites: 1, Attempts: 1}
	result := ComponentManagedMutationResult{Disposition: ComponentManagedMutationDesiredProjectionStaged}
	if err := registration.ValidateMutationReceipt(plan, result, ComponentManagedMutationReceipt{Usage: usage, EffectClasses: []ComponentManagedEffectClass{"effect_b", "effect_a"}}); err != nil {
		t.Fatalf("valid effect receipt: %v", err)
	}
	if err := registration.ValidateMutationReceipt(plan, result, ComponentManagedMutationReceipt{Usage: usage, EffectClasses: []ComponentManagedEffectClass{"effect_a", "effect_a"}}); err == nil || !strings.Contains(err.Error(), "duplicated") {
		t.Fatalf("duplicate effect receipt error = %v", err)
	}
}

func TestComponentManagedStructuredDescriptorsRequireExplicitBounds(t *testing.T) {
	const maxBatchRows = 10
	if err := (ComponentManagedRowQuery{Table: "WorkflowStatus", Columns: []string{"Name"}, Equal: ComponentRow{"Owner": "acme"}}).Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "maximum rows") {
		t.Fatalf("unbounded query error = %v", err)
	}
	query := ComponentManagedRowQuery{
		Table: "WorkflowStatus", Columns: []string{"Name"}, Equal: ComponentRow{"Owner": "acme"},
		OrderBy: []ComponentManagedOrder{{Column: "Name", Direction: ComponentManagedOrderAscending}}, LogicalKeyColumns: []string{"Name"}, MaximumRows: 10,
	}
	if err := query.Validate(maxBatchRows); err != nil {
		t.Fatalf("bounded query: %v", err)
	}
	query.Columns = []string{"Label"}
	if err := query.Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "projected columns") {
		t.Fatalf("missing logical-key projection error = %v", err)
	}
	query.Columns = []string{"Name"}
	query.MaximumRows = maxBatchRows + 1
	if err := query.Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "1..10") {
		t.Fatalf("over-budget query error = %v", err)
	}
	if err := (ComponentManagedUpdate{Table: "WorkflowStatus", Match: ComponentRow{"Owner": "acme"}, Values: ComponentRow{"Label": "Open"}}).Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "maximum affected rows") {
		t.Fatalf("unbounded update error = %v", err)
	}
	if err := (ComponentManagedUpdate{Table: "WorkflowStatus", Match: ComponentRow{"Owner": "acme"}, Values: ComponentRow{"Label": "Open"}, MaximumAffectedRows: maxBatchRows + 1}).Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "1..10") {
		t.Fatalf("over-budget update error = %v", err)
	}
	if err := (ComponentManagedDelete{Table: "WorkflowStatus", Match: ComponentRow{"Owner": "acme"}, MaximumAffectedRows: 1}).Validate(maxBatchRows); err != nil {
		t.Fatalf("bounded delete: %v", err)
	}
	if err := (ComponentManagedDelete{Table: "WorkflowStatus", Match: ComponentRow{"Owner": "acme"}, MaximumAffectedRows: maxBatchRows + 1}).Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "1..10") {
		t.Fatalf("over-budget delete error = %v", err)
	}
	if err := (ComponentManagedInsertBatch{Table: "WorkflowStatus", Rows: []ComponentManagedInsertRow{{LogicalKey: ComponentRow{"Owner": "acme", "Name": "open"}, Values: ComponentRow{"Owner": "acme", "Name": "open"}}}}).Validate(maxBatchRows); err != nil {
		t.Fatalf("bounded insert: %v", err)
	}
	if err := (ComponentManagedInsertBatch{Table: "WorkflowStatus", Rows: []ComponentManagedInsertRow{{LogicalKey: ComponentRow{"Owner": 1}, Values: ComponentRow{"Owner": "1"}}}}).Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "exactly match") {
		t.Fatalf("typed logical-key mismatch error = %v", err)
	}
}

func TestComponentManagedKeysetRowQueryValidatesContinuation(t *testing.T) {
	const maxBatchRows = 10
	query := ComponentManagedKeysetRowQuery{
		Query: ComponentManagedRowQuery{
			Table: "WorkflowStatus", Columns: []string{"Owner", "Name"}, Equal: ComponentRow{"Tenant": "acme"},
			OrderBy: []ComponentManagedOrder{
				{Column: "Owner", Direction: ComponentManagedOrderAscending},
				{Column: "Name", Direction: ComponentManagedOrderDescending},
			},
			LogicalKeyColumns: []string{"Owner", "Name"},
			MaximumRows:       maxBatchRows,
		},
		ContinueAfter: ComponentRow{"Owner": "acme", "Name": "open"},
	}
	if err := query.Validate(maxBatchRows); err != nil {
		t.Fatalf("valid continuation: %v", err)
	}
	query.ContinueAfter = ComponentRow{"Owner": "acme"}
	if err := query.Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "exactly one value") {
		t.Fatalf("incomplete continuation error = %v", err)
	}
	query.ContinueAfter = ComponentRow{"Owner": "acme", "Name": nil}
	if err := query.Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "cannot be nil") {
		t.Fatalf("nil continuation error = %v", err)
	}
	query.ContinueAfter = ComponentRow{"Owner": "acme", "Name": "open"}
	query.Query.OrderBy[1].Column = "Label"
	if err := query.Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "match logical key") {
		t.Fatalf("unstable continuation order error = %v", err)
	}
	query.Query.OrderBy[1].Column = "Name"
	query.Query.OrderBy = nil
	if err := query.Validate(maxBatchRows); err == nil || !strings.Contains(err.Error(), "requires order columns") {
		t.Fatalf("pagination without order error = %v", err)
	}
}

func TestComponentManagedRowQueryKeepsLegacyUnkeyedShape(t *testing.T) {
	query := ComponentManagedRowQuery{
		"WorkflowStatus",
		[]string{"Name"},
		ComponentRow{"Owner": "acme"},
		nil,
		nil,
		1,
	}
	if err := query.Validate(1); err != nil {
		t.Fatal(err)
	}
}

func TestComponentManagedTransactionOperationProviderRequiresCallbacks(t *testing.T) {
	if err := (ComponentManagedTransactionOperationProvider{}).Validate(); err == nil || !strings.Contains(err.Error(), "Mutate, Project") {
		t.Fatalf("missing callbacks error = %v", err)
	}
	provider := ComponentManagedTransactionOperationProvider{
		Mutate: func(context.Context, ComponentManagedPlan, ComponentManagedTransactionMutationStore) (ComponentManagedMutationResult, error) {
			return ComponentManagedMutationResult{Disposition: ComponentManagedMutationNoChange}, nil
		},
		Project: func(context.Context, ComponentManagedPlan, ComponentManagedTransactionReadStore) (ComponentManagedProjection, error) {
			return ComponentManagedProjection{Identity: []byte("id"), Expected: []byte("same"), Live: []byte("same")}, nil
		},
	}
	if err := provider.Validate(); err != nil {
		t.Fatalf("complete provider: %v", err)
	}
}

func TestComponentManagedPlannedTransactionOperationProviderRequiresCallbacks(t *testing.T) {
	if err := (ComponentManagedPlannedTransactionOperationProvider{}).Validate(); err == nil ||
		!strings.Contains(err.Error(), "Plan, Mutate, Project") {
		t.Fatalf("empty provider validation = %v", err)
	}
	provider := ComponentManagedPlannedTransactionOperationProvider{
		Plan: func(context.Context, ComponentManagedTransactionReadStore) (ComponentManagedPlan, error) {
			return ComponentManagedPlan{}, nil
		},
		Mutate: func(context.Context, ComponentManagedPlan, ComponentManagedTransactionMutationStore) (ComponentManagedMutationResult, error) {
			return ComponentManagedMutationResult{}, nil
		},
		Project: func(context.Context, ComponentManagedPlan, ComponentManagedTransactionReadStore) (ComponentManagedProjection, error) {
			return ComponentManagedProjection{}, nil
		},
	}
	if err := provider.Validate(); err != nil {
		t.Fatal(err)
	}
}

func TestComponentManagedSetLifecycleRequestRejectsNonCanonicalLiteral(t *testing.T) {
	request := ComponentManagedLifecycleRequest{
		Set:                 &ComponentManagedSetRequest{PackName: "acme", Kind: ComponentKindApprovalProcess},
		DesiredBusinessKeys: []string{"z", "a"},
	}
	if err := request.Validate(ComponentManagedScopeSet); err == nil || !strings.Contains(err.Error(), "sorted") {
		t.Fatalf("non-canonical set request error = %v", err)
	}
}

func validComponentManagedBudget() ComponentManagedBudget {
	return ComponentManagedBudget{
		MaxTargets: 100, MaxCurrentItems: 100, MaxDesiredItems: 100, MaxDeltaRows: 100,
		MaxMutationBytes: 1 << 20, MaxMutationStatements: 100, MaxBatchRows: 100,
		MaxEvidenceWrites: 1, MaxEffectCalls: 10,
		MaxLockWait: 2 * time.Second, MaxElapsed: 10 * time.Second, MaxAttempts: 3,
	}
}

func completeComponentManagedLifecycleProvider() ComponentManagedLifecycleProvider {
	return ComponentManagedLifecycleProvider{
		Plan: func(context.Context, ComponentManagedScope, ComponentManagedLifecycleRequest, ComponentManagedReadStore) (ComponentManagedPlan, error) {
			return ComponentManagedPlan{}, nil
		},
		Mutate: func(context.Context, ComponentManagedScope, ComponentManagedLifecycleRequest, ComponentManagedPlan, ComponentManagedMutationStore) (ComponentManagedMutationResult, error) {
			return ComponentManagedMutationResult{Disposition: ComponentManagedMutationNoChange}, nil
		},
		Project: func(context.Context, ComponentManagedScope, ComponentManagedLifecycleRequest, ComponentManagedReadStore) (ComponentManagedProjection, error) {
			return ComponentManagedProjection{}, nil
		},
	}
}

func mustComponentManagedPlan(t *testing.T, capabilities []ComponentManagedCapability) ComponentManagedPlan {
	t.Helper()
	return mustComponentManagedPlanFromSpec(t, ComponentManagedPlanSpec{
		Targets:              []ComponentManagedTarget{{TargetDomain: "workflow/status", PhysicalKeyBytes: []byte("open")}},
		RequiredCapabilities: capabilities,
		ProjectionIdentity:   []byte("workflow/acme"),
		OpaquePlan:           []byte(`{}`),
	})
}

func mustComponentManagedPlanFromSpec(t *testing.T, spec ComponentManagedPlanSpec) ComponentManagedPlan {
	t.Helper()
	plan, err := NewComponentManagedPlan(spec)
	if err != nil {
		t.Fatal(err)
	}
	return plan
}
