package layoutauthoringv1

import (
	"encoding/json"
	"reflect"
	"strings"
	"testing"
)

func TestCatalogViewRefValidation(t *testing.T) {
	valid := validViewRef(42)
	if err := ValidateCatalogViewRef(valid); err != nil {
		t.Fatalf("valid ref rejected: %v", err)
	}
	for name, ref := range map[string]CatalogViewRef{
		"zero revision":   {AuthoringViewDigest: valid.AuthoringViewDigest},
		"missing digest":  {CatalogRevision: 42},
		"uppercase":       {CatalogRevision: 42, AuthoringViewDigest: "sha256:ABCDEF0123456789abcdef0123456789abcdef0123456789abcdef0123456789"},
		"wrong algorithm": {CatalogRevision: 42, AuthoringViewDigest: "sha512:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"},
		"wrong length":    {CatalogRevision: 42, AuthoringViewDigest: "sha256:00"},
	} {
		t.Run(name, func(t *testing.T) {
			if err := ValidateCatalogViewRef(ref); err == nil {
				t.Fatal("malformed ref unexpectedly accepted")
			}
		})
	}
}

func TestCommandStatusPublicationInvariants(t *testing.T) {
	request := validSubmit(validCreateOperation())
	digest, err := RequestDigest(request)
	if err != nil {
		t.Fatal(err)
	}
	base := CommandStatus{CommandID: request.CommandID, RequestDigest: digest, Receipts: validReceipts(50)}

	for _, state := range []CommandState{StateAccepted, StateInProgress} {
		status := base
		status.State = state
		if err := ValidateCommandStatusForSubmit(request, status); err != nil {
			t.Fatalf("%s rejected: %v", state, err)
		}
		status.CatalogViewRef = pointer(validViewRef(50))
		if err := ValidateCommandStatusForSubmit(request, status); err == nil {
			t.Fatalf("%s claimed a published view", state)
		}
		status.CatalogViewRef = nil
		status.Failure = &CommandFailure{Category: FailureConflict, Message: "stale aggregate"}
		if err := ValidateCommandStatusForSubmit(request, status); err == nil {
			t.Fatalf("%s claimed a terminal failure", state)
		}
	}

	failed := base
	failed.State = StateFailed
	failed.Failure = &CommandFailure{Category: FailureConflict, Message: "stale aggregate", FieldReference: "/create/expected_workspace_version"}
	if err := ValidateCommandStatusForSubmit(request, failed); err != nil {
		t.Fatalf("FAILED rejected: %v", err)
	}
	failed.CatalogViewRef = pointer(validViewRef(50))
	if err := ValidateCommandStatusForSubmit(request, failed); err == nil {
		t.Fatal("FAILED claimed a published view")
	}

	succeeded := base
	succeeded.State = StateSucceeded
	succeeded.CatalogViewRef = pointer(validViewRef(50))
	succeeded.Result = &CommandResult{Create: &CreateLayoutResult{Layout: LayoutSnapshot{
		Document: validDocument(), OrderedListFields: []string{"Name", "Amount"},
		ResourceVersion: "resource-v2", AggregateVersion: "aggregate-v2", WorkspaceVersion: "workspace-v2",
	}}}
	if err := ValidateCommandStatusForSubmit(request, succeeded); err != nil {
		t.Fatalf("SUCCEEDED rejected: %v", err)
	}
	withFailure := succeeded
	withFailure.Failure = &CommandFailure{Category: FailureConflict, Message: "stale aggregate"}
	if err := ValidateCommandStatusForSubmit(request, withFailure); err == nil {
		t.Fatal("SUCCEEDED claimed a terminal failure")
	}

	withoutRef := succeeded
	withoutRef.CatalogViewRef = nil
	if err := ValidateCommandStatusForSubmit(request, withoutRef); err == nil {
		t.Fatal("SUCCEEDED without ref accepted")
	}
	mismatch := succeeded
	mismatch.CatalogViewRef = pointer(validViewRef(51))
	if err := ValidateCommandStatusForSubmit(request, mismatch); err == nil {
		t.Fatal("receipt/view-ref revision mismatch accepted")
	}
	notAdvanced := succeeded
	notAdvanced.CatalogViewRef = pointer(validViewRef(request.BaseCatalogRevision))
	notAdvanced.Receipts = validReceipts(request.BaseCatalogRevision)
	if err := ValidateCommandStatusForSubmit(request, notAdvanced); err == nil {
		t.Fatal("successful status reused the pre-mutation Catalog revision")
	}
	if err := ValidateSubmitResponse(request, SubmitResponse{Command: notAdvanced}); err == nil {
		t.Fatal("successful submit response reused the pre-mutation Catalog revision")
	}
	wrongResult := succeeded
	wrongResult.Result = &CommandResult{Delete: &DeleteLayoutResult{LayoutID: "layout-main", ObjectID: "Account", WorkspaceVersion: "workspace-v2"}}
	if err := ValidateCommandStatusForSubmit(request, wrongResult); err == nil {
		t.Fatal("operation/result arm mismatch accepted")
	}
}

func TestSuccessfulResultsAreCorrelatedToAcceptedOperation(t *testing.T) {
	rename := "Renamed"
	changes := preserveChanges()
	changes.Name = Change[string]{Mode: ChangeSet, Value: &rename}
	updatedDocument := validDocument()
	updatedDocument.Name = rename
	cloneDocument := validDocument()
	cloneDocument.LayoutID = "layout-copy"
	cloneDocument.Name = "Copy"
	profileID := "profile-a"

	tests := map[string]struct {
		operation CommandOperation
		result    CommandResult
		mismatch  func(*CommandResult)
	}{
		"create aggregate and ordered list fields": {
			operation: validCreateOperation(),
			result:    CommandResult{Create: &CreateLayoutResult{Layout: validSnapshot(validDocument())}},
			mismatch:  func(result *CommandResult) { result.Create.Layout.Document.ObjectID = "Contact" },
		},
		"update aggregate": {
			operation: CommandOperation{Update: &UpdateLayout{LayoutID: "layout-main", ExpectedAggregateVersion: "aggregate-v1", Changes: changes, ListFields: FieldIDListChange{Mode: ChangePreserve}}},
			result:    CommandResult{Update: &UpdateLayoutResult{Layout: validSnapshot(updatedDocument)}},
			mismatch:  func(result *CommandResult) { result.Update.Layout.Document.LayoutID = "layout-other" },
		},
		"clone target identity": {
			operation: CommandOperation{Clone: &CloneLayout{SourceLayoutID: "layout-main", SourceResourceVersion: "resource-v1", TargetLayoutID: "layout-copy", TargetName: "Copy", ExpectedWorkspaceVersion: "workspace-v1"}},
			result:    CommandResult{Clone: &CloneLayoutResult{Layout: validSnapshot(cloneDocument)}},
			mismatch:  func(result *CommandResult) { result.Clone.Layout.Document.Name = "Wrong copy" },
		},
		"set-default aggregate": {
			operation: CommandOperation{SetDefault: &SetDefaultLayout{ObjectID: "Account", LayoutID: "layout-main", ExpectedWorkspaceVersion: "workspace-v1"}},
			result:    CommandResult{SetDefault: &SetDefaultLayoutResult{ObjectID: "Account", LayoutID: "layout-main", WorkspaceVersion: "workspace-v2"}},
			mismatch:  func(result *CommandResult) { result.SetDefault.LayoutID = "layout-other" },
		},
		"delete aggregate": {
			operation: CommandOperation{Delete: &DeleteLayout{LayoutID: "layout-main", ExpectedResourceVersion: "resource-v1", ExpectedWorkspaceVersion: "workspace-v1"}},
			result:    CommandResult{Delete: &DeleteLayoutResult{LayoutID: "layout-main", ObjectID: "Account", WorkspaceVersion: "workspace-v2"}},
			mismatch:  func(result *CommandResult) { result.Delete.LayoutID = "layout-other" },
		},
		"restore aggregate and history": {
			operation: CommandOperation{Restore: &RestoreLayout{LayoutID: "layout-main", HistoryVersion: 7, ExpectedResourceVersion: "resource-v1"}},
			result:    CommandResult{Restore: &RestoreLayoutResult{Layout: validSnapshot(validDocument()), HistoryVersion: 7}},
			mismatch:  func(result *CommandResult) { result.Restore.HistoryVersion++ },
		},
		"assignment-upsert target and selectors": {
			operation: CommandOperation{AssignmentUpsert: &UpsertLayoutAssignment{ObjectID: "Account", LayoutID: "layout-main", Selectors: AssignmentSelectors{ProfileID: &profileID}, ExpectedWorkspaceVersion: "workspace-v1"}},
			result: CommandResult{AssignmentUpsert: &UpsertLayoutAssignmentResult{Assignment: LayoutAssignmentSummary{
				AssignmentID: "assignment-a", ObjectID: "Account", LayoutID: "layout-main", Selectors: AssignmentSelectors{ProfileID: &profileID}, Specificity: 1, ResourceVersion: "resource-v2",
			}, WorkspaceVersion: "workspace-v2"}},
			mismatch: func(result *CommandResult) { result.AssignmentUpsert.Assignment.Selectors.RoleID = pointer("role-a") },
		},
		"assignment-delete identity": {
			operation: CommandOperation{AssignmentDelete: &DeleteLayoutAssignment{AssignmentID: "assignment-a", ExpectedResourceVersion: "resource-v1", ExpectedWorkspaceVersion: "workspace-v1"}},
			result:    CommandResult{AssignmentDelete: &DeleteLayoutAssignmentResult{AssignmentID: "assignment-a", ObjectID: "Account", WorkspaceVersion: "workspace-v2"}},
			mismatch:  func(result *CommandResult) { result.AssignmentDelete.AssignmentID = "assignment-other" },
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			request := validSubmit(test.operation)
			status := succeededStatus(t, request, test.result)
			if err := ValidateCommandStatusForSubmit(request, status); err != nil {
				t.Fatalf("correlated result rejected: %v", err)
			}
			test.mismatch(status.Result)
			if err := ValidateCommandStatusForSubmit(request, status); err == nil {
				t.Fatal("structurally valid result for a different request identity was accepted")
			}
		})
	}
}

func TestGuaranteedWorkspaceMutationsAdvanceExpectedVersion(t *testing.T) {
	cloneDocument := validDocument()
	cloneDocument.LayoutID = "layout-copy"
	cloneDocument.Name = "Copy"

	tests := map[string]struct {
		operation CommandOperation
		result    CommandResult
		stale     func(*CommandResult)
	}{
		"create": {
			operation: validCreateOperation(),
			result:    CommandResult{Create: &CreateLayoutResult{Layout: validSnapshot(validDocument())}},
			stale: func(result *CommandResult) {
				result.Create.Layout.WorkspaceVersion = "workspace-v1"
			},
		},
		"clone": {
			operation: CommandOperation{Clone: &CloneLayout{SourceLayoutID: "layout-main", SourceResourceVersion: "resource-v1", TargetLayoutID: "layout-copy", TargetName: "Copy", ExpectedWorkspaceVersion: "workspace-v1"}},
			result:    CommandResult{Clone: &CloneLayoutResult{Layout: validSnapshot(cloneDocument)}},
			stale: func(result *CommandResult) {
				result.Clone.Layout.WorkspaceVersion = "workspace-v1"
			},
		},
		"delete": {
			operation: CommandOperation{Delete: &DeleteLayout{LayoutID: "layout-main", ExpectedResourceVersion: "resource-v1", ExpectedWorkspaceVersion: "workspace-v1"}},
			result:    CommandResult{Delete: &DeleteLayoutResult{LayoutID: "layout-main", ObjectID: "Account", WorkspaceVersion: "workspace-v2"}},
			stale: func(result *CommandResult) {
				result.Delete.WorkspaceVersion = "workspace-v1"
			},
		},
		"assignment delete": {
			operation: CommandOperation{AssignmentDelete: &DeleteLayoutAssignment{AssignmentID: "assignment-a", ExpectedResourceVersion: "resource-v1", ExpectedWorkspaceVersion: "workspace-v1"}},
			result:    CommandResult{AssignmentDelete: &DeleteLayoutAssignmentResult{AssignmentID: "assignment-a", ObjectID: "Account", WorkspaceVersion: "workspace-v2"}},
			stale: func(result *CommandResult) {
				result.AssignmentDelete.WorkspaceVersion = "workspace-v1"
			},
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			request := validSubmit(test.operation)
			if err := ValidateCommandStatusForSubmit(request, succeededStatus(t, request, test.result)); err != nil {
				t.Fatalf("advanced workspace version rejected: %v", err)
			}
			test.stale(&test.result)
			if err := ValidateCommandStatusForSubmit(request, succeededStatus(t, request, test.result)); err == nil {
				t.Fatal("pre-mutation workspace version returned by successful mutation")
			}
		})
	}
}

func TestAssignmentUpsertResultCannotRepresentWildcardOwnership(t *testing.T) {
	profileID := "profile-a"
	status := CommandStatus{
		CommandID: "command-1", RequestDigest: "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
		State: StateSucceeded, CatalogViewRef: pointer(validViewRef(50)), Receipts: validReceipts(50),
		Result: &CommandResult{AssignmentUpsert: &UpsertLayoutAssignmentResult{
			Assignment: LayoutAssignmentSummary{
				AssignmentID: "assignment-a", ObjectID: "Account", LayoutID: "layout-main",
				Selectors: AssignmentSelectors{ProfileID: &profileID}, Specificity: 1, ResourceVersion: "resource-v2",
			},
			WorkspaceVersion: "workspace-v2",
		}},
	}
	request := GetCommandRequest{CommandID: status.CommandID, RequestDigest: status.RequestDigest}
	if err := ValidateCommandStatus(request, status); err != nil {
		t.Fatalf("specific assignment result rejected: %v", err)
	}
	status.Result.AssignmentUpsert.Assignment.Selectors = AssignmentSelectors{}
	status.Result.AssignmentUpsert.Assignment.Specificity = 0
	if err := ValidateCommandStatus(request, status); err == nil {
		t.Fatal("selectorless assignment-upsert result claimed SetDefault wildcard ownership")
	}
}

func TestCreateResultMustMatchRequestedDocumentAndListFieldEffect(t *testing.T) {
	request := validSubmit(validCreateOperation())
	result := CommandResult{Create: &CreateLayoutResult{Layout: validSnapshot(validDocument())}}
	status := succeededStatus(t, request, result)
	status.Result.Create.Layout.Document.Name = "Different"
	if err := ValidateCommandStatusForSubmit(request, status); err == nil {
		t.Fatal("create result with a different document was accepted")
	}

	status = succeededStatus(t, request, result)
	status.Result.Create.Layout.OrderedListFields = []string{"Amount", "Name"}
	if err := ValidateCommandStatusForSubmit(request, status); err == nil {
		t.Fatal("create result with different ordered ListFields was accepted")
	}

	request.Operation.Create.ListFields = FieldIDListChange{Mode: ChangeClear}
	request.Operation.Create.ExpectedWorkspaceVersion = pointer("workspace-v1")
	status = succeededStatus(t, request, result)
	if err := ValidateCommandStatusForSubmit(request, status); err == nil {
		t.Fatal("CLEAR result with non-empty ordered ListFields was accepted")
	}
}

func TestUpdateResultMustMatchEveryRequestKnownEffect(t *testing.T) {
	document := validDocument()
	name := "Renamed"
	emptyCompactFields := []string{}
	changes := preserveChanges()
	changes.Name = Change[string]{Mode: ChangeSet, Value: &name}
	changes.Kind = Change[string]{Mode: ChangeClear}
	changes.CompactFields = Change[[]string]{Mode: ChangeSet, Value: &emptyCompactFields}
	changes.QuickActions = Change[ActionPlacement]{Mode: ChangeClear}
	document.Name = name
	document.Kind = ""
	document.CompactFields = []string{}
	document.QuickActions = ActionPlacement{Actions: []LayoutAction{}}
	listFields := []string{"Amount"}
	request := validSubmit(CommandOperation{Update: &UpdateLayout{
		LayoutID: "layout-main", ExpectedAggregateVersion: "aggregate-v1", Changes: changes,
		ListFields: FieldIDListChange{Mode: ChangeSet, Value: &listFields},
	}})
	result := CommandResult{Update: &UpdateLayoutResult{Layout: validSnapshot(document)}}
	result.Update.Layout.OrderedListFields = append([]string(nil), listFields...)
	if err := ValidateCommandStatusForSubmit(request, succeededStatus(t, request, result)); err != nil {
		t.Fatalf("matching update effects rejected: %v", err)
	}

	tests := map[string]func(*LayoutSnapshot){
		"set scalar":     func(snapshot *LayoutSnapshot) { snapshot.Document.Name = "Old" },
		"clear scalar":   func(snapshot *LayoutSnapshot) { snapshot.Document.Kind = "Detail" },
		"set collection": func(snapshot *LayoutSnapshot) { snapshot.Document.CompactFields = []string{"Name"} },
		"clear struct":   func(snapshot *LayoutSnapshot) { snapshot.Document.QuickActions.Authored = true },
		"list fields":    func(snapshot *LayoutSnapshot) { snapshot.OrderedListFields = []string{"Name"} },
	}
	for name, mutate := range tests {
		t.Run(name, func(t *testing.T) {
			changed := result
			mutate(&changed.Update.Layout)
			if err := ValidateCommandStatusForSubmit(request, succeededStatus(t, request, changed)); err == nil {
				t.Fatal("update result with a mismatched request-known effect was accepted")
			}
		})
	}
}

func TestSubmitRelationalValidatorsRejectInvalidRequests(t *testing.T) {
	validOperation := validCreateOperation()
	tests := map[string]SubmitRequest{
		"zero operation arms": validSubmit(CommandOperation{}),
		"multiple operation arms": validSubmit(CommandOperation{
			Create: validOperation.Create,
			Delete: &DeleteLayout{LayoutID: "layout-main", ExpectedResourceVersion: "resource-v1", ExpectedWorkspaceVersion: "workspace-v1"},
		}),
		"zero base revision": func() SubmitRequest {
			request := validSubmit(validOperation)
			request.BaseCatalogRevision = 0
			return request
		}(),
		"invalid idempotency key": func() SubmitRequest {
			request := validSubmit(validOperation)
			request.IdempotencyKey = "bad\nkey"
			return request
		}(),
	}
	for name, request := range tests {
		t.Run(name, func(t *testing.T) {
			digest, err := RequestDigest(request)
			if err != nil {
				t.Fatal(err)
			}
			status := CommandStatus{
				CommandID: request.CommandID, RequestDigest: digest, State: StateAccepted,
				Receipts: validReceipts(50),
			}
			if err := ValidateSubmitResponse(request, SubmitResponse{Command: status}); err == nil {
				t.Fatal("submit response validated an invalid request")
			}
			if err := ValidateCommandStatusForSubmit(request, status); err == nil {
				t.Fatal("command status validated an invalid submit request")
			}
		})
	}
}

func TestSubmitResponseHonorsWaitMode(t *testing.T) {
	request := validSubmit(validCreateOperation())
	digest, err := RequestDigest(request)
	if err != nil {
		t.Fatal(err)
	}

	for _, state := range []CommandState{StateAccepted, StateInProgress} {
		status := CommandStatus{
			CommandID: request.CommandID, RequestDigest: digest, State: state,
			Receipts: validReceipts(50),
		}
		if err := ValidateSubmitResponse(request, SubmitResponse{Command: status}); err == nil {
			t.Fatalf("WAIT_FOR_TERMINAL accepted %s submit response", state)
		}

		returnAfterAcceptance := request
		returnAfterAcceptance.WaitMode = ReturnAfterAcceptance
		if err := ValidateSubmitResponse(returnAfterAcceptance, SubmitResponse{Command: status}); err != nil {
			t.Fatalf("RETURN_AFTER_ACCEPTANCE rejected %s submit response: %v", state, err)
		}
	}

	succeeded := succeededStatus(t, request, CommandResult{
		Create: &CreateLayoutResult{Layout: validSnapshot(validDocument())},
	})
	if err := ValidateSubmitResponse(request, SubmitResponse{Command: succeeded}); err != nil {
		t.Fatalf("WAIT_FOR_TERMINAL rejected SUCCEEDED submit response: %v", err)
	}

	failed := CommandStatus{
		CommandID: request.CommandID, RequestDigest: digest, State: StateFailed,
		Failure:  &CommandFailure{Category: FailureConflict, Message: "stale aggregate"},
		Receipts: validReceipts(50),
	}
	if err := ValidateSubmitResponse(request, SubmitResponse{Command: failed}); err != nil {
		t.Fatalf("WAIT_FOR_TERMINAL rejected FAILED submit response: %v", err)
	}
}

func TestCommandFailureContractIsClosedAndFieldPreserving(t *testing.T) {
	wantCategories := [4]FailureCategory{
		FailureInvalidRequest,
		FailureForbidden,
		FailureMissing,
		FailureConflict,
	}
	if got := AllCommandFailureCategories(); got != wantCategories {
		t.Fatalf("failure categories = %v, want %v", got, wantCategories)
	}

	request := validSubmit(validCreateOperation())
	digest, err := RequestDigest(request)
	if err != nil {
		t.Fatal(err)
	}
	for _, category := range wantCategories {
		t.Run(string(category), func(t *testing.T) {
			failure := CommandFailure{
				Category: category, Message: "Layout mutation was rejected.",
				FieldReference: "/create/document/sections/0/fields",
			}
			status := CommandStatus{
				CommandID: request.CommandID, RequestDigest: digest, State: StateFailed,
				Failure: &failure, Receipts: validReceipts(request.BaseCatalogRevision),
			}
			if err := ValidateCommandStatusForSubmit(request, status); err != nil {
				t.Fatalf("valid terminal failure rejected: %v", err)
			}

			encoded, err := json.Marshal(status)
			if err != nil {
				t.Fatal(err)
			}
			if strings.Contains(string(encoded), "retryable") || strings.Contains(string(encoded), "diagnostic") {
				t.Fatalf("stale failure vocabulary serialized: %s", encoded)
			}
			var decoded CommandStatus
			if err := json.Unmarshal(encoded, &decoded); err != nil {
				t.Fatal(err)
			}
			if !reflect.DeepEqual(decoded, status) {
				t.Fatalf("terminal failure changed during round trip\n got: %#v\nwant: %#v", decoded, status)
			}
		})
	}
}

func TestCommandFailureFieldReferenceUsesSemanticOperationPointer(t *testing.T) {
	request := validSubmit(validCreateOperation())
	digest, err := RequestDigest(request)
	if err != nil {
		t.Fatal(err)
	}
	status := CommandStatus{
		CommandID: request.CommandID, RequestDigest: digest, State: StateFailed,
		Failure:  &CommandFailure{Category: FailureInvalidRequest, Message: "Layout mutation was rejected."},
		Receipts: validReceipts(request.BaseCatalogRevision),
	}

	for _, reference := range []string{
		"/create/document/name",
		"/create/document/sections/0/fields",
		"/create/document/sections/0/fields/1",
		"/create/list_fields/value/0",
		"/create/expected_workspace_version",
	} {
		status.Failure.FieldReference = reference
		if err := ValidateCommandStatusForSubmit(request, status); err != nil {
			t.Fatalf("semantic operation pointer %q rejected: %v", reference, err)
		}
	}

	profileID := "profile-a"
	updatedName := "Renamed"
	updateChanges := preserveChanges()
	updateChanges.Name = Change[string]{Mode: ChangeSet, Value: &updatedName}
	for name, test := range map[string]struct {
		operation CommandOperation
		reference string
	}{
		"create": {validCreateOperation(), "/create/document/name"},
		"update": {CommandOperation{Update: &UpdateLayout{
			LayoutID: "layout-main", ExpectedAggregateVersion: "aggregate-v1",
			Changes: updateChanges, ListFields: FieldIDListChange{Mode: ChangePreserve},
		}}, "/update/expected_aggregate_version"},
		"clone": {CommandOperation{Clone: &CloneLayout{
			SourceLayoutID: "layout-main", SourceResourceVersion: "resource-v1", TargetLayoutID: "layout-copy",
			TargetName: "Copy", ExpectedWorkspaceVersion: "workspace-v1",
		}}, "/clone/source_resource_version"},
		"set default": {CommandOperation{SetDefault: &SetDefaultLayout{
			ObjectID: "Account", LayoutID: "layout-main", ExpectedWorkspaceVersion: "workspace-v1",
		}}, "/set_default/layout_id"},
		"delete": {CommandOperation{Delete: &DeleteLayout{
			LayoutID: "layout-main", ExpectedResourceVersion: "resource-v1", ExpectedWorkspaceVersion: "workspace-v1",
		}}, "/delete/expected_resource_version"},
		"restore": {CommandOperation{Restore: &RestoreLayout{
			LayoutID: "layout-main", HistoryVersion: 7, ExpectedResourceVersion: "resource-v1",
		}}, "/restore/history_version"},
		"assignment upsert": {CommandOperation{AssignmentUpsert: &UpsertLayoutAssignment{
			ObjectID: "Account", LayoutID: "layout-main", Selectors: AssignmentSelectors{ProfileID: &profileID},
			ExpectedWorkspaceVersion: "workspace-v1",
		}}, "/assignment_upsert/selectors/profile_id"},
		"assignment delete": {CommandOperation{AssignmentDelete: &DeleteLayoutAssignment{
			AssignmentID: "assignment-a", ExpectedResourceVersion: "resource-v1", ExpectedWorkspaceVersion: "workspace-v1",
		}}, "/assignment_delete/assignment_id"},
	} {
		t.Run(name, func(t *testing.T) {
			request := validSubmit(test.operation)
			digest, err := RequestDigest(request)
			if err != nil {
				t.Fatal(err)
			}
			status := CommandStatus{
				CommandID: request.CommandID, RequestDigest: digest, State: StateFailed,
				Failure: &CommandFailure{
					Category: FailureInvalidRequest, Message: "Layout mutation was rejected.", FieldReference: test.reference,
				},
				Receipts: validReceipts(request.BaseCatalogRevision),
			}
			if err := ValidateCommandStatusForSubmit(request, status); err != nil {
				t.Fatalf("operation-correlated pointer %q rejected: %v", test.reference, err)
			}
		})
	}

	// A standalone durable status read lacks the original operation value, but
	// it still rejects arbitrary paths by validating against the semantic type.
	getRequest := GetCommandRequest{CommandID: status.CommandID, RequestDigest: status.RequestDigest}
	status.Failure.FieldReference = "/update/expected_aggregate_version"
	if err := ValidateCommandStatus(getRequest, status); err != nil {
		t.Fatalf("schema-valid status pointer rejected without submit context: %v", err)
	}
	status.Failure.FieldReference = "/update/internal_sql"
	if err := ValidateCommandStatus(getRequest, status); err == nil {
		t.Fatal("status read accepted a Core-internal path outside the semantic request shape")
	}

	preserveRequest := validSubmit(CommandOperation{Create: &CreateLayout{
		Document: validDocument(), ListFields: FieldIDListChange{Mode: ChangePreserve},
	}})
	preserveDigest, err := RequestDigest(preserveRequest)
	if err != nil {
		t.Fatal(err)
	}
	status.CommandID = preserveRequest.CommandID
	status.RequestDigest = preserveDigest
	status.Failure.FieldReference = "/create/expected_workspace_version"
	if err := ValidateCommandStatusForSubmit(preserveRequest, status); err == nil {
		t.Fatal("submit-bound status referenced an optional field absent from the accepted operation")
	}
}

func TestCommandFailureRejectsRecoveryAndMalformedPublicFacts(t *testing.T) {
	request := validSubmit(validCreateOperation())
	digest, err := RequestDigest(request)
	if err != nil {
		t.Fatal(err)
	}
	valid := CommandStatus{
		CommandID: request.CommandID, RequestDigest: digest, State: StateFailed,
		Failure:  &CommandFailure{Category: FailureConflict, Message: "stale aggregate", FieldReference: "/create/expected_workspace_version"},
		Receipts: validReceipts(request.BaseCatalogRevision),
	}
	invalid := map[string]func(*CommandStatus){
		"missing failure": func(status *CommandStatus) { status.Failure = nil },
		"result with failure": func(status *CommandStatus) {
			status.Result = &CommandResult{Create: &CreateLayoutResult{Layout: validSnapshot(validDocument())}}
		},
		"published view with failure":  func(status *CommandStatus) { status.CatalogViewRef = pointer(validViewRef(51)) },
		"empty category":               func(status *CommandStatus) { status.Failure.Category = "" },
		"unknown category":             func(status *CommandStatus) { status.Failure.Category = FailureCategory("RETRY_LATER") },
		"unauthenticated admission":    func(status *CommandStatus) { status.Failure.Category = FailureUnauthenticated },
		"canceled call":                func(status *CommandStatus) { status.Failure.Category = FailureCanceled },
		"deadline call":                func(status *CommandStatus) { status.Failure.Category = FailureDeadlineExceeded },
		"resource admission":           func(status *CommandStatus) { status.Failure.Category = FailureResourceExhausted },
		"incompatible admission":       func(status *CommandStatus) { status.Failure.Category = FailureIncompatible },
		"transient unavailable":        func(status *CommandStatus) { status.Failure.Category = FailureUnavailable },
		"internal recovery":            func(status *CommandStatus) { status.Failure.Category = FailureInternal },
		"stale admission":              func(status *CommandStatus) { status.Failure.Category = FailureStaleState },
		"empty message":                func(status *CommandStatus) { status.Failure.Message = "" },
		"blank message":                func(status *CommandStatus) { status.Failure.Message = " \t\n" },
		"leading message whitespace":   func(status *CommandStatus) { status.Failure.Message = " rejected" },
		"trailing message whitespace":  func(status *CommandStatus) { status.Failure.Message = "rejected " },
		"message line feed":            func(status *CommandStatus) { status.Failure.Message = "first\nsecond" },
		"message carriage return":      func(status *CommandStatus) { status.Failure.Message = "first\rsecond" },
		"message tab":                  func(status *CommandStatus) { status.Failure.Message = "first\tsecond" },
		"message control":              func(status *CommandStatus) { status.Failure.Message = "first\x00second" },
		"message zero width format":    func(status *CommandStatus) { status.Failure.Message = "first\u200bsecond" },
		"message bidi override":        func(status *CommandStatus) { status.Failure.Message = "first\u202esecond" },
		"message line separator":       func(status *CommandStatus) { status.Failure.Message = "first\u2028second" },
		"message paragraph separator":  func(status *CommandStatus) { status.Failure.Message = "first\u2029second" },
		"oversized message":            func(status *CommandStatus) { status.Failure.Message = strings.Repeat("m", MaxFailureMessageBytes+1) },
		"missing pointer root":         func(status *CommandStatus) { status.Failure.FieldReference = "create/document/name" },
		"L1 field name":                func(status *CommandStatus) { status.Failure.FieldReference = "/create/layoutName" },
		"dot path":                     func(status *CommandStatus) { status.Failure.FieldReference = "operation.create.document.name" },
		"pre-producer aggregate alias": func(status *CommandStatus) { status.Failure.FieldReference = "aggregateVersion" },
		"pre-producer bracket path":    func(status *CommandStatus) { status.Failure.FieldReference = "sections[0].fields" },
		"pre-producer wire fixture":    func(status *CommandStatus) { status.Failure.FieldReference = "operation.set_default.layout_id" },
		"wrong operation arm":          func(status *CommandStatus) { status.Failure.FieldReference = "/update/expected_aggregate_version" },
		"unknown semantic field":       func(status *CommandStatus) { status.Failure.FieldReference = "/create/internal_sql" },
		"out of range index":           func(status *CommandStatus) { status.Failure.FieldReference = "/create/document/sections/9/fields" },
		"non-canonical index":          func(status *CommandStatus) { status.Failure.FieldReference = "/create/document/sections/00/fields" },
		"empty segment":                func(status *CommandStatus) { status.Failure.FieldReference = "/create/document//name" },
		"field control":                func(status *CommandStatus) { status.Failure.FieldReference = "/create/document/layout\nname" },
		"field format character":       func(status *CommandStatus) { status.Failure.FieldReference = "/create/document/layout\u202ename" },
		"oversized field": func(status *CommandStatus) {
			status.Failure.FieldReference = "/create/" + strings.Repeat("f", MaxFailureFieldBytes)
		},
	}
	for name, mutate := range invalid {
		t.Run(name, func(t *testing.T) {
			status := valid
			failure := *valid.Failure
			status.Failure = &failure
			mutate(&status)
			if err := ValidateCommandStatusForSubmit(request, status); err == nil {
				t.Fatal("invalid terminal failure accepted")
			}
		})
	}

	exact := valid
	exactFailure := *valid.Failure
	exact.Failure = &exactFailure
	exact.Failure.Message = strings.Repeat("m", MaxFailureMessageBytes)
	exact.Failure.FieldReference = "/create/document/sections/0/fields/1"
	if err := ValidateCommandStatusForSubmit(request, exact); err != nil {
		t.Fatalf("bounded failure rejected: %v", err)
	}

	unicodeMessage := valid
	unicodeFailure := *valid.Failure
	unicodeMessage.Failure = &unicodeFailure
	unicodeMessage.Failure.Message = "布局 “Account” 已冲突。"
	if err := ValidateCommandStatusForSubmit(request, unicodeMessage); err != nil {
		t.Fatalf("ordinary Unicode failure message rejected: %v", err)
	}
}

func validSnapshot(document LayoutDocument) LayoutSnapshot {
	return LayoutSnapshot{
		Document: document, OrderedListFields: []string{"Name", "Amount"},
		ResourceVersion: "resource-v2", AggregateVersion: "aggregate-v2", WorkspaceVersion: "workspace-v2",
	}
}

func succeededStatus(t *testing.T, request SubmitRequest, result CommandResult) CommandStatus {
	t.Helper()
	digest, err := RequestDigest(request)
	if err != nil {
		t.Fatal(err)
	}
	return CommandStatus{
		CommandID: request.CommandID, RequestDigest: digest, State: StateSucceeded,
		Result: &result, CatalogViewRef: pointer(validViewRef(50)), Receipts: validReceipts(50),
	}
}

func TestReadResponsesRequireExactViewAndReceiptRevision(t *testing.T) {
	requested := validViewRef(50)
	stored := StoredLayout{
		Document: validDocument(), OrderedListFields: []string{"Name"},
		Lifecycle: LayoutLifecycleState{ProvenanceKind: "admin"}, ResourceVersion: "resource-v1",
		AggregateVersion: "aggregate-v1", WorkspaceVersion: "workspace-v1",
		ViewRef: requested, Receipts: validReceipts(50),
	}
	request := StoredLayoutRequest{LayoutID: "layout-main", ViewRef: &requested}
	if err := ValidateStoredLayoutResponse(request, stored); err != nil {
		t.Fatalf("exact read rejected: %v", err)
	}
	for name, mutate := range map[string]func(*StoredLayout){
		"digest": func(value *StoredLayout) {
			value.ViewRef.AuthoringViewDigest = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
		},
		"revision": func(value *StoredLayout) { value.ViewRef.CatalogRevision++ },
		"receipt":  func(value *StoredLayout) { value.Receipts.CatalogRevision++ },
	} {
		t.Run(name, func(t *testing.T) {
			value := stored
			mutate(&value)
			if err := ValidateStoredLayoutResponse(request, value); err == nil {
				t.Fatal("mismatched exact read unexpectedly accepted")
			}
		})
	}
}

func TestPublicCommandStateSetIsExhaustive(t *testing.T) {
	want := [4]CommandState{StateAccepted, StateInProgress, StateSucceeded, StateFailed}
	if got := AllCommandStates(); !reflect.DeepEqual(got, want) {
		t.Fatalf("AllCommandStates() = %v, want %v", got, want)
	}
	for _, state := range want {
		if !state.Valid() {
			t.Fatalf("public state %q is invalid", state)
		}
	}
	for _, state := range []CommandState{"", "PROCESSING", "COMPLETED", "RETRYING", "UNKNOWN"} {
		if state.Valid() {
			t.Fatalf("non-public state %q is valid", state)
		}
	}
}
