package layoutauthoringv1

import (
	"context"
	"reflect"
	"regexp"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"

	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
)

var (
	digestPattern  = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`)
	decimalPattern = regexp.MustCompile(`^-?(0|[1-9][0-9]*)(\.[0-9]+)?$`)
)

func ValidateSubmitRequest(ctx context.Context, request SubmitRequest) error {
	if err := common.ValidateRequestContext(ctx); err != nil {
		return err
	}
	return validateSubmitRequestBody(request)
}

func validateSubmitRequestBody(request SubmitRequest) error {
	if !validOpaque(request.CommandID, MaxCommandIdentifierBytes) {
		if len(request.CommandID) > MaxCommandIdentifierBytes {
			return exhausted(LimitCommandID, MaxCommandIdentifierBytes)
		}
		return invalid(InvalidComponentCommandID)
	}
	if !validOpaque(request.IdempotencyKey, MaxIdempotencyKeyBytes) ||
		request.BaseCatalogRevision == 0 || !request.WaitMode.Valid() {
		return invalid(InvalidComponentCommandData)
	}
	if err := validateCommandOperation(request.Operation, false); err != nil {
		return err
	}
	if SemanticRequestSize(request) > MaxSemanticRequestBytes {
		return exhausted(LimitSemanticRequest, MaxSemanticRequestBytes)
	}
	return nil
}

func ValidateGetCommandRequest(ctx context.Context, request GetCommandRequest) error {
	if err := common.ValidateRequestContext(ctx); err != nil {
		return err
	}
	if !validOpaque(request.CommandID, MaxCommandIdentifierBytes) || !digestPattern.MatchString(request.RequestDigest) {
		return invalid(InvalidComponentCommandData)
	}
	return nil
}

func ValidateSubmitResponse(request SubmitRequest, response SubmitResponse) error {
	if err := validateSubmitRequestBody(request); err != nil {
		return internal()
	}
	digest, err := RequestDigest(request)
	if err != nil || response.Command.CommandID != request.CommandID || response.Command.RequestDigest != digest {
		return internal()
	}
	if err := validateCommandStatusShape(response.Command, &request.Operation, request.BaseCatalogRevision); err != nil {
		return err
	}
	// WAIT_FOR_TERMINAL is a promise made by this submit call. Durable status
	// reads may still observe non-terminal states, but this response may not.
	if request.WaitMode == WaitForTerminal && !response.Command.State.Terminal() {
		return internal()
	}
	if SemanticResponseSize(response) > MaxSemanticResponseBytes {
		return exhausted(LimitSemanticResponse, MaxSemanticResponseBytes)
	}
	return nil
}

func ValidateCommandStatus(request GetCommandRequest, response CommandStatus) error {
	if response.CommandID != request.CommandID || response.RequestDigest != request.RequestDigest {
		return internal()
	}
	if err := validateCommandStatusShape(response, nil, 0); err != nil {
		return err
	}
	if SemanticResponseSize(response) > MaxSemanticResponseBytes {
		return exhausted(LimitSemanticResponse, MaxSemanticResponseBytes)
	}
	return nil
}

// ValidateCommandStatusForSubmit additionally binds a durable status to the
// original operation arm and base Catalog revision retained at acceptance.
func ValidateCommandStatusForSubmit(request SubmitRequest, response CommandStatus) error {
	if err := validateSubmitRequestBody(request); err != nil {
		return internal()
	}
	digest, err := RequestDigest(request)
	if err != nil || response.CommandID != request.CommandID || response.RequestDigest != digest {
		return internal()
	}
	if err := validateCommandStatusShape(response, &request.Operation, request.BaseCatalogRevision); err != nil {
		return err
	}
	if SemanticResponseSize(response) > MaxSemanticResponseBytes {
		return exhausted(LimitSemanticResponse, MaxSemanticResponseBytes)
	}
	return nil
}

func ValidateCatalogViewRef(ref CatalogViewRef) error {
	if ref.CatalogRevision == 0 || !digestPattern.MatchString(ref.AuthoringViewDigest) {
		return invalid(InvalidComponentCommandData)
	}
	return nil
}

func ValidateWorkspaceRequest(ctx context.Context, request WorkspaceRequest) error {
	if err := common.ValidateRequestContext(ctx); err != nil {
		return err
	}
	if !common.ValidObjectIdentifier(request.ObjectID) {
		return invalid(InvalidComponentObjectID)
	}
	if request.ViewRef != nil {
		return ValidateCatalogViewRef(*request.ViewRef)
	}
	return nil
}

func ValidateWorkspaceResponse(request WorkspaceRequest, response Workspace) error {
	if response.ObjectID != request.ObjectID || !common.ValidObjectIdentifier(response.ObjectID) ||
		!validVersion(response.WorkspaceVersion) {
		return internal()
	}
	if err := validateReadView(request.ViewRef, response.ViewRef, response.Receipts); err != nil {
		return err
	}
	if len(response.Layouts) > MaxWorkspaceLayouts {
		return exhausted(LimitRecordCount, MaxWorkspaceLayouts)
	}
	if len(response.Assignments) > MaxWorkspaceAssignments {
		return exhausted(LimitRecordCount, MaxWorkspaceAssignments)
	}
	layoutIDs := make(map[string]struct{}, len(response.Layouts))
	previousLayoutID := ""
	defaultLayoutID := ""
	for index, layout := range response.Layouts {
		if err := validateLayoutSummary(layout, response.ObjectID); err != nil {
			return err
		}
		if index > 0 && !routingIdentityLess(previousLayoutID, layout.LayoutID) {
			return internal()
		}
		previousLayoutID = layout.LayoutID
		layoutKey := routingIdentityKey(layout.LayoutID)
		if _, duplicate := layoutIDs[layoutKey]; duplicate {
			return internal()
		}
		layoutIDs[layoutKey] = struct{}{}
		if layout.IsDefault {
			if defaultLayoutID != "" {
				return internal()
			}
			defaultLayoutID = layout.LayoutID
		}
	}
	previousAssignmentID := ""
	selectorTuples := make(map[string]struct{}, len(response.Assignments))
	wildcardLayoutID := ""
	for index, assignment := range response.Assignments {
		if err := validateAssignmentSummary(assignment, response.ObjectID); err != nil {
			return err
		}
		if _, exists := layoutIDs[routingIdentityKey(assignment.LayoutID)]; !exists ||
			(index > 0 && assignment.AssignmentID <= previousAssignmentID) {
			return internal()
		}
		previousAssignmentID = assignment.AssignmentID
		tuple := selectorTuple(assignment.Selectors)
		if _, duplicate := selectorTuples[tuple]; duplicate {
			return internal()
		}
		selectorTuples[tuple] = struct{}{}
		if selectorCount(assignment.Selectors) == 0 {
			wildcardLayoutID = assignment.LayoutID
		}
	}
	if (defaultLayoutID == "") != (wildcardLayoutID == "") ||
		(defaultLayoutID != "" && routingIdentityKey(defaultLayoutID) != routingIdentityKey(wildcardLayoutID)) {
		return internal()
	}
	if SemanticResponseSize(response) > MaxSemanticResponseBytes {
		return exhausted(LimitSemanticResponse, MaxSemanticResponseBytes)
	}
	return nil
}

func ValidateStoredLayoutRequest(ctx context.Context, request StoredLayoutRequest) error {
	if err := common.ValidateRequestContext(ctx); err != nil {
		return err
	}
	if !validIdentifier(request.LayoutID) {
		return invalid(InvalidComponentRecordID)
	}
	if request.ViewRef != nil {
		return ValidateCatalogViewRef(*request.ViewRef)
	}
	return nil
}

// ValidateCreateContext validates read-only facts without deriving field rules.
func ValidateCreateContext(context CreateContext) error {
	// Reject impossible-size inputs before allocating the case-folded set.
	if len(context.RequiredFields) > MaxCreateRequiredFields {
		return exhausted(LimitFieldCount, MaxCreateRequiredFields)
	}
	if SemanticResponseSize(context) > MaxSemanticResponseBytes {
		return exhausted(LimitSemanticResponse, MaxSemanticResponseBytes)
	}
	if err := validateFieldIDs(context.RequiredFields, MaxCreateRequiredFields, false); err != nil {
		return err
	}
	for i := 1; i < len(context.RequiredFields); i++ {
		if context.RequiredFields[i-1] >= context.RequiredFields[i] {
			return invalid(InvalidComponentFieldValue)
		}
	}
	return nil
}

func ValidateStoredLayoutResponse(request StoredLayoutRequest, response StoredLayout) error {
	if response.Document.LayoutID != request.LayoutID {
		return internal()
	}
	if err := ValidateLayoutDocument(response.Document); err != nil {
		return internal()
	}
	if err := validateFieldIDs(response.OrderedListFields, MaxListFields, false); err != nil {
		return internal()
	}
	if response.CreateContext != nil {
		if err := ValidateCreateContext(*response.CreateContext); err != nil {
			return internal()
		}
	}
	if err := validateLifecycle(response.Lifecycle); err != nil ||
		!validVersion(response.ResourceVersion) || !validVersion(response.AggregateVersion) ||
		!validVersion(response.WorkspaceVersion) {
		return internal()
	}
	if err := validateReadView(request.ViewRef, response.ViewRef, response.Receipts); err != nil {
		return err
	}
	if SemanticResponseSize(response) > MaxSemanticResponseBytes {
		return exhausted(LimitSemanticResponse, MaxSemanticResponseBytes)
	}
	return nil
}

func ValidateLayoutDocument(document LayoutDocument) error {
	if !validIdentifier(document.LayoutID) || !common.ValidObjectIdentifier(document.ObjectID) ||
		!validExactVocabulary(document.Name, MaxNameBytes, false) || !validExactVocabulary(document.Kind, MaxKindBytes, true) {
		return invalid(InvalidComponentCommandData)
	}
	if document.CompactFields == nil || document.Tabs == nil || document.Sections == nil ||
		document.RelatedLists == nil || document.RecordPanels == nil ||
		document.HeaderActions.Actions == nil || document.QuickActions.Actions == nil ||
		document.RecordStates == nil || document.PrimaryHeaderActions == nil {
		return invalid(InvalidComponentCommandData)
	}
	if err := validateDocumentCollections(document); err != nil {
		return err
	}
	if LayoutDocumentSemanticSize(document) > MaxLayoutDocumentSemanticBytes {
		return exhausted(LimitSemanticRequest, MaxLayoutDocumentSemanticBytes)
	}
	return nil
}

// ValidateOrderedListFields validates the shared stored and mutation grammar
// for an object's ordered list fields.
func ValidateOrderedListFields(fields []string) error {
	return validateFieldIDs(fields, MaxListFields, false)
}

func ValidateLayoutDocumentChanges(changes LayoutDocumentChanges) error {
	if err := validateChange(&changes.Name, false, func(value string) error {
		if !validExactVocabulary(value, MaxNameBytes, false) {
			return invalid(InvalidComponentCommandData)
		}
		return nil
	}); err != nil {
		return err
	}
	// Core stores Layout kind as a value string. CLEAR is the one canonical
	// update expression for the stored empty value; SET therefore requires a
	// non-empty exact vocabulary value.
	if err := validateChange(&changes.Kind, true, func(value string) error {
		if !validExactVocabulary(value, MaxKindBytes, false) {
			return invalid(InvalidComponentCommandData)
		}
		return nil
	}); err != nil {
		return err
	}
	if err := validateChange(&changes.LayoutVersion, true, func(uint32) error { return nil }); err != nil {
		return err
	}
	if err := validateChange(&changes.CompactFields, true, func(value []string) error {
		return validateFieldIDs(value, MaxCompactFields, false)
	}); err != nil {
		return err
	}
	if err := validateChange(&changes.Tabs, true, func(value []string) error {
		return validateOpaqueList(value, MaxTabs)
	}); err != nil {
		return err
	}
	knownTabs := tabsKnownAfterChange(changes.Tabs)
	budget := valueBudget{}
	if err := validateChange(&changes.Sections, true, func(value []LayoutSection) error {
		return validateSections(value, knownTabs, &budget)
	}); err != nil {
		return err
	}
	if err := validateChange(&changes.RelatedLists, true, func(value []RelatedList) error {
		return validateRelatedLists(value)
	}); err != nil {
		return err
	}
	if err := validateChange(&changes.RecordPanels, true, func(value []RecordPanel) error {
		return validateRecordPanels(value, &budget)
	}); err != nil {
		return err
	}
	if err := validateChange(&changes.HeaderActions, true, func(value ActionPlacement) error {
		return validateActionPlacement(value, &budget)
	}); err != nil {
		return err
	}
	if err := validateChange(&changes.QuickActions, true, func(value ActionPlacement) error {
		return validateActionPlacement(value, &budget)
	}); err != nil {
		return err
	}
	if err := validateChange(&changes.RecordStates, true, validateRecordStates); err != nil {
		return err
	}
	if err := validateChange(&changes.PrimaryHeaderActions, true, func(value []string) error {
		return validateOpaqueList(value, MaxPrimaryHeaderActions)
	}); err != nil {
		return err
	}
	return nil
}

// tabsKnownAfterChange returns nil only when the existing tab set is
// intentionally preserved and therefore cannot be related to a simultaneous
// Sections SET at admission time. SET and CLEAR describe the complete
// post-operation tab set.
func tabsKnownAfterChange(change Change[[]string]) map[string]struct{} {
	if change.Mode == ChangePreserve {
		return nil
	}
	tabs := make(map[string]struct{})
	if change.Mode == ChangeSet && change.Value != nil {
		for _, tab := range *change.Value {
			tabs[tab] = struct{}{}
		}
	}
	return tabs
}

func validateCommandOperation(operation CommandOperation, allowNoopUpdate bool) error {
	arms := operationArmCount(operation)
	if arms != 1 {
		return invalid(InvalidComponentOperation)
	}
	switch {
	case operation.Create != nil:
		value := operation.Create
		if err := ValidateLayoutDocument(value.Document); err != nil {
			return err
		}
		if err := validateFieldIDListChange(value.ListFields); err != nil {
			return err
		}
		switch value.ListFields.Mode {
		case ChangePreserve:
			if value.ExpectedWorkspaceVersion != nil {
				return invalid(InvalidComponentOperation)
			}
		case ChangeSet, ChangeClear:
			if value.ExpectedWorkspaceVersion == nil || !validVersion(*value.ExpectedWorkspaceVersion) {
				return invalid(InvalidComponentOperation)
			}
		}
		return nil
	case operation.Update != nil:
		value := operation.Update
		if !validIdentifier(value.LayoutID) || !validVersion(value.ExpectedAggregateVersion) {
			return invalid(InvalidComponentOperation)
		}
		if err := ValidateLayoutDocumentChanges(value.Changes); err != nil {
			return err
		}
		if err := validateFieldIDListChange(value.ListFields); err != nil {
			return err
		}
		if !allowNoopUpdate && !changesAnything(value.Changes) && value.ListFields.Mode == ChangePreserve {
			return invalid(InvalidComponentOperation)
		}
		return nil
	case operation.Clone != nil:
		value := operation.Clone
		if !validIdentifier(value.SourceLayoutID) || !validVersion(value.SourceResourceVersion) ||
			!validIdentifier(value.TargetLayoutID) || value.SourceLayoutID == value.TargetLayoutID ||
			!validExactVocabulary(value.TargetName, MaxNameBytes, false) || !validVersion(value.ExpectedWorkspaceVersion) {
			return invalid(InvalidComponentOperation)
		}
	case operation.SetDefault != nil:
		value := operation.SetDefault
		if !common.ValidObjectIdentifier(value.ObjectID) || !validIdentifier(value.LayoutID) ||
			!validVersion(value.ExpectedWorkspaceVersion) {
			return invalid(InvalidComponentOperation)
		}
	case operation.Delete != nil:
		value := operation.Delete
		if !validIdentifier(value.LayoutID) || !validVersion(value.ExpectedResourceVersion) ||
			!validVersion(value.ExpectedWorkspaceVersion) {
			return invalid(InvalidComponentOperation)
		}
	case operation.Restore != nil:
		value := operation.Restore
		if !validIdentifier(value.LayoutID) || value.HistoryVersion == 0 ||
			!validVersion(value.ExpectedResourceVersion) {
			return invalid(InvalidComponentOperation)
		}
	case operation.AssignmentUpsert != nil:
		value := operation.AssignmentUpsert
		if !common.ValidObjectIdentifier(value.ObjectID) || !validIdentifier(value.LayoutID) ||
			!validVersion(value.ExpectedWorkspaceVersion) || selectorCount(value.Selectors) == 0 ||
			!validSelectors(value.Selectors) {
			return invalid(InvalidComponentOperation)
		}
	case operation.AssignmentDelete != nil:
		value := operation.AssignmentDelete
		if !validIdentifier(value.AssignmentID) || !validVersion(value.ExpectedResourceVersion) ||
			!validVersion(value.ExpectedWorkspaceVersion) {
			return invalid(InvalidComponentOperation)
		}
	}
	return nil
}

// ValidateCommandOperation validates the shared neutral Layout operation
// grammar without adding a transport or command lifecycle.
func ValidateCommandOperation(operation CommandOperation) error {
	return validateCommandOperation(operation, false)
}

// ValidateCommandOperationShape validates the complete operation grammar but
// permits a preserve-only update. Durable v1 commands reject that no-op;
// synchronous callers can return Changed=false without creating a command.
func ValidateCommandOperationShape(operation CommandOperation) error {
	return validateCommandOperation(operation, true)
}

func operationArmCount(operation CommandOperation) int {
	count := 0
	for _, present := range []bool{
		operation.Create != nil, operation.Update != nil, operation.Clone != nil,
		operation.SetDefault != nil, operation.Delete != nil, operation.Restore != nil,
		operation.AssignmentUpsert != nil, operation.AssignmentDelete != nil,
	} {
		if present {
			count++
		}
	}
	return count
}

func validateDocumentCollections(document LayoutDocument) error {
	if err := validateFieldIDs(document.CompactFields, MaxCompactFields, false); err != nil {
		return err
	}
	if err := validateOpaqueList(document.Tabs, MaxTabs); err != nil {
		return err
	}
	tabs := make(map[string]struct{}, len(document.Tabs))
	for _, tab := range document.Tabs {
		tabs[tab] = struct{}{}
	}
	budget := valueBudget{}
	if err := validateSections(document.Sections, tabs, &budget); err != nil {
		return err
	}
	if err := validateRelatedLists(document.RelatedLists); err != nil {
		return err
	}
	if err := validateRecordPanels(document.RecordPanels, &budget); err != nil {
		return err
	}
	if err := validateActionPlacement(document.HeaderActions, &budget); err != nil {
		return err
	}
	if err := validateActionPlacement(document.QuickActions, &budget); err != nil {
		return err
	}
	if err := validateRecordStates(document.RecordStates); err != nil {
		return err
	}
	return validateOpaqueList(document.PrimaryHeaderActions, MaxPrimaryHeaderActions)
}

func validateSections(sections []LayoutSection, tabs map[string]struct{}, budget *valueBudget) error {
	if sections == nil {
		return invalid(InvalidComponentCommandData)
	}
	if len(sections) > MaxSections {
		return exhausted(LimitOperationCount, MaxSections)
	}
	if err := validateFieldBehaviorBudget(sections); err != nil {
		return err
	}
	seenSections := make(map[string]struct{}, len(sections))
	seenFields := make(map[string]struct{})
	for _, section := range sections {
		if !validIdentifier(section.SectionID) || !validText(section.Label, MaxNameBytes) ||
			!validOptionalExactVocabulary(section.Kind, MaxKindBytes) || section.Columns > 2 ||
			!validOptionalText(section.VisibilityCondition, MaxExpressionBytes) ||
			!validOptionalExactVocabulary(section.ComponentName, MaxOpaqueIdentifierBytes) {
			return invalid(InvalidComponentCommandData)
		}
		if _, duplicate := seenSections[section.SectionID]; duplicate {
			return invalid(InvalidComponentCommandData)
		}
		seenSections[section.SectionID] = struct{}{}
		if section.TabID != nil {
			if !validExactVocabulary(*section.TabID, MaxOpaqueIdentifierBytes, true) {
				return invalid(InvalidComponentCommandData)
			}
			if tabs != nil && *section.TabID != "" {
				if _, exists := tabs[*section.TabID]; !exists {
					return invalid(InvalidComponentCommandData)
				}
			}
		}
		if section.ComponentConfiguration == nil || section.Fields == nil ||
			section.FieldLabels == nil || section.FieldBehaviors == nil {
			return invalid(InvalidComponentCommandData)
		}
		if err := validateNamedValues(section.ComponentConfiguration, MaxConfigurationEntries, budget); err != nil {
			return err
		}
		if len(section.Fields) > MaxFieldsPerSection {
			return exhausted(LimitFieldCount, MaxFieldsPerSection)
		}
		localFields := make(map[string]struct{}, len(section.Fields))
		for _, fieldReference := range section.Fields {
			if !validFieldReference(fieldReference) {
				return invalid(InvalidComponentFieldValue)
			}
			key := strings.ToLower(fieldReference)
			if _, duplicate := localFields[key]; duplicate {
				return invalid(InvalidComponentFieldValue)
			}
			if _, duplicate := seenFields[key]; duplicate {
				return invalid(InvalidComponentFieldValue)
			}
			localFields[key] = struct{}{}
			seenFields[key] = struct{}{}
		}
		if err := validateFieldLabels(section.FieldLabels); err != nil {
			return err
		}
		if err := validateFieldBehaviors(section.FieldBehaviors); err != nil {
			return err
		}
	}
	return nil
}

func validateFieldLabels(labels []FieldLabel) error {
	if len(labels) > MaxFieldLabelsPerSection {
		return exhausted(LimitFieldCount, MaxFieldLabelsPerSection)
	}
	previous := ""
	seenReferences := make(map[string]struct{}, len(labels))
	for index, label := range labels {
		if !validFieldReference(label.FieldReference) || !validText(label.Label, MaxNameBytes) ||
			(index > 0 && label.FieldReference <= previous) {
			return invalid(InvalidComponentFieldValue)
		}
		key := strings.ToLower(label.FieldReference)
		if _, duplicate := seenReferences[key]; duplicate {
			return invalid(InvalidComponentFieldValue)
		}
		seenReferences[key] = struct{}{}
		previous = label.FieldReference
	}
	return nil
}

func validateFieldBehaviorBudget(sections []LayoutSection) error {
	nodes := 0
	for _, section := range sections {
		if len(section.FieldBehaviors) > MaxFieldBehaviorGroupsPerSection {
			return exhausted(LimitFieldCount, MaxFieldBehaviorGroupsPerSection)
		}
		nodes += len(section.FieldBehaviors)
		if nodes > MaxFieldBehaviorNodesPerDocument {
			return exhausted(LimitFieldCount, MaxFieldBehaviorNodesPerDocument)
		}
		for _, group := range section.FieldBehaviors {
			if group.Behaviors == nil {
				continue
			}
			if *group.Behaviors == nil {
				return invalid(InvalidComponentFieldValue)
			}
			if len(*group.Behaviors) > MaxFieldBehaviorsPerField {
				return exhausted(LimitFieldCount, MaxFieldBehaviorsPerField)
			}
			nodes += len(*group.Behaviors)
			if nodes > MaxFieldBehaviorNodesPerDocument {
				return exhausted(LimitFieldCount, MaxFieldBehaviorNodesPerDocument)
			}
		}
	}
	return nil
}

func validateFieldBehaviors(groups []FieldBehaviorGroup) error {
	if len(groups) > MaxFieldBehaviorGroupsPerSection {
		return exhausted(LimitFieldCount, MaxFieldBehaviorGroupsPerSection)
	}
	previousReference := ""
	seenReferenceCase := make(map[string]string, len(groups))
	for index, group := range groups {
		if !validFieldReference(group.FieldReference) ||
			(index > 0 && group.FieldReference <= previousReference) {
			return invalid(InvalidComponentFieldValue)
		}
		caseKey := strings.ToLower(group.FieldReference)
		if first, exists := seenReferenceCase[caseKey]; exists && first != group.FieldReference {
			return invalid(InvalidComponentFieldValue)
		}
		seenReferenceCase[caseKey] = group.FieldReference
		previousReference = group.FieldReference
		if group.Behaviors == nil {
			continue
		}
		behaviors := *group.Behaviors
		if behaviors == nil {
			return invalid(InvalidComponentFieldValue)
		}
		if len(behaviors) > MaxFieldBehaviorsPerField {
			return exhausted(LimitFieldCount, MaxFieldBehaviorsPerField)
		}
		previousMode := ""
		for behaviorIndex, behavior := range behaviors {
			if !validExactVocabulary(behavior.Mode, MaxKindBytes, true) ||
				!validExactVocabulary(behavior.Behavior, MaxKindBytes, true) ||
				(behaviorIndex > 0 && behavior.Mode <= previousMode) {
				return invalid(InvalidComponentFieldValue)
			}
			previousMode = behavior.Mode
		}
	}
	return nil
}

func validateRelatedLists(lists []RelatedList) error {
	if lists == nil {
		return invalid(InvalidComponentCommandData)
	}
	if len(lists) > MaxRelatedLists {
		return exhausted(LimitOperationCount, MaxRelatedLists)
	}
	seenIDs := make(map[string]struct{}, len(lists))
	seenTargets := make(map[string]struct{}, len(lists))
	for _, list := range lists {
		if !validIdentifier(list.RelatedListID) || !validRequiredAuthoredText(list.Label, MaxNameBytes) ||
			!validText(list.SecondaryLabel, MaxNameBytes) ||
			!common.ValidObjectIdentifier(list.ObjectID) || !common.ValidFieldIdentifier(list.LookupFieldID) {
			return invalid(InvalidComponentCommandData)
		}
		if _, duplicate := seenIDs[list.RelatedListID]; duplicate {
			return invalid(InvalidComponentCommandData)
		}
		seenIDs[list.RelatedListID] = struct{}{}
		target := strings.ToLower(list.ObjectID + "\x00" + list.LookupFieldID)
		if _, duplicate := seenTargets[target]; duplicate {
			return invalid(InvalidComponentCommandData)
		}
		seenTargets[target] = struct{}{}
		if err := validateFieldIDs(list.FieldIDs, MaxFieldsPerRelatedList, false); err != nil {
			return err
		}
		if list.PrefillMappings == nil {
			return invalid(InvalidComponentCommandData)
		}
		if len(list.PrefillMappings) > MaxPrefillMappings {
			return exhausted(LimitFieldCount, MaxPrefillMappings)
		}
		previous := ""
		seenTargets := make(map[string]struct{}, len(list.PrefillMappings))
		for index, mapping := range list.PrefillMappings {
			if !common.ValidFieldIdentifier(mapping.TargetFieldID) ||
				!common.ValidFieldIdentifier(mapping.SourceFieldID) ||
				(index > 0 && mapping.TargetFieldID <= previous) {
				return invalid(InvalidComponentFieldValue)
			}
			target := strings.ToLower(mapping.TargetFieldID)
			if _, duplicate := seenTargets[target]; duplicate {
				return invalid(InvalidComponentFieldValue)
			}
			seenTargets[target] = struct{}{}
			previous = mapping.TargetFieldID
		}
	}
	return nil
}

func validateRecordPanels(panels []RecordPanel, budget *valueBudget) error {
	if panels == nil {
		return invalid(InvalidComponentCommandData)
	}
	if len(panels) > MaxRecordPanels {
		return exhausted(LimitOperationCount, MaxRecordPanels)
	}
	seen := make(map[string]struct{}, len(panels))
	for _, panel := range panels {
		if !validIdentifier(panel.PanelID) || !validExactVocabulary(panel.RendererID, MaxOpaqueIdentifierBytes, false) ||
			!validExactVocabulary(panel.Position, MaxKindBytes, true) || !validExactVocabulary(panel.TabID, MaxOpaqueIdentifierBytes, true) ||
			!validText(panel.TabLabel, MaxNameBytes) || !validText(panel.VisibilityCondition, MaxExpressionBytes) {
			return invalid(InvalidComponentCommandData)
		}
		if _, duplicate := seen[panel.PanelID]; duplicate {
			return invalid(InvalidComponentCommandData)
		}
		seen[panel.PanelID] = struct{}{}
		if panel.Position == "" || panel.Position == "tab" {
			if panel.TabID == "" || panel.TabLabel == "" {
				return invalid(InvalidComponentCommandData)
			}
		}
		if panel.RendererConfiguration == nil {
			return invalid(InvalidComponentCommandData)
		}
		if err := validateNamedValues(panel.RendererConfiguration, MaxConfigurationEntries, budget); err != nil {
			return err
		}
	}
	return nil
}

func validateActionPlacement(placement ActionPlacement, budget *valueBudget) error {
	if placement.Actions == nil || (!placement.Authored && len(placement.Actions) != 0) {
		return invalid(InvalidComponentCommandData)
	}
	if len(placement.Actions) > MaxActionsPerPlacement {
		return exhausted(LimitOperationCount, MaxActionsPerPlacement)
	}
	seen := make(map[string]struct{}, len(placement.Actions))
	for _, action := range placement.Actions {
		if !validExactVocabulary(action.Name, MaxOpaqueIdentifierBytes, false) || !validRequiredAuthoredText(action.Label, MaxNameBytes) ||
			!validExactVocabulary(action.Kind, MaxKindBytes, false) || !validOptionalText(action.Icon, MaxNameBytes) ||
			!validOptionalObjectID(action.TargetObjectID) || !validOptionalExactVocabulary(action.Component, MaxOpaqueIdentifierBytes) ||
			!validOptionalText(action.VisibilityCondition, MaxExpressionBytes) {
			return invalid(InvalidComponentCommandData)
		}
		if _, duplicate := seen[action.Name]; duplicate {
			return invalid(InvalidComponentCommandData)
		}
		seen[action.Name] = struct{}{}
		if action.Configuration == nil || action.Extensions == nil {
			return invalid(InvalidComponentCommandData)
		}
		if err := validateNamedValues(action.Configuration, MaxConfigurationEntries, budget); err != nil {
			return err
		}
		if err := validateNamedValues(action.Extensions, MaxActionExtensions, budget); err != nil {
			return err
		}
		for _, extension := range action.Extensions {
			switch extension.Name {
			case "name", "label", "type", "kind", "icon", "targetObject", "target_object_id",
				"config", "configuration", "visibilityCondition", "visibility_condition", "component":
				return invalid(InvalidComponentCommandData)
			}
		}
	}
	return nil
}

func validateRecordStates(rules []RecordStateRule) error {
	if rules == nil {
		return invalid(InvalidComponentCommandData)
	}
	if len(rules) > MaxRecordStates {
		return exhausted(LimitOperationCount, MaxRecordStates)
	}
	seenRules := make(map[string]struct{}, len(rules))
	for _, rule := range rules {
		if rule.Links == nil || rule.ActionOverrides == nil || !validIdentifier(rule.RuleID) ||
			!validRequiredAuthoredText(rule.Condition, MaxExpressionBytes) {
			return invalid(InvalidComponentCommandData)
		}
		if len(rule.Links) > MaxRecordStateLinks {
			return exhausted(LimitOperationCount, MaxRecordStateLinks)
		}
		if len(rule.ActionOverrides) > MaxRecordStateActionOverrides {
			return exhausted(LimitOperationCount, MaxRecordStateActionOverrides)
		}
		if _, duplicate := seenRules[rule.RuleID]; duplicate {
			return invalid(InvalidComponentCommandData)
		}
		seenRules[rule.RuleID] = struct{}{}
		linkIDs := make(map[string]struct{}, len(rule.Links))
		for _, link := range rule.Links {
			if !validIdentifier(link.LinkID) || !validRequiredAuthoredText(link.Label, MaxNameBytes) ||
				!common.ValidFieldIdentifier(link.ReferenceFieldID) ||
				(link.ObjectID != "" && !common.ValidObjectIdentifier(link.ObjectID)) ||
				!validExactVocabulary(link.Placement, MaxKindBytes, true) {
				return invalid(InvalidComponentCommandData)
			}
			if _, duplicate := linkIDs[link.LinkID]; duplicate {
				return invalid(InvalidComponentCommandData)
			}
			linkIDs[link.LinkID] = struct{}{}
		}
		if rule.Banner != nil {
			if rule.Banner.LinkIDs == nil || !validExactVocabulary(rule.Banner.Severity, MaxKindBytes, false) ||
				!validRequiredAuthoredText(rule.Banner.Title, MaxNameBytes) ||
				!validText(rule.Banner.Body, MaxDescriptionBytes) {
				return invalid(InvalidComponentCommandData)
			}
			if len(rule.Banner.LinkIDs) > MaxRecordStateLinks {
				return exhausted(LimitOperationCount, MaxRecordStateLinks)
			}
			seenBannerLinks := make(map[string]struct{}, len(rule.Banner.LinkIDs))
			for _, linkID := range rule.Banner.LinkIDs {
				if _, exists := linkIDs[linkID]; !exists {
					return invalid(InvalidComponentCommandData)
				}
				if _, duplicate := seenBannerLinks[linkID]; duplicate {
					return invalid(InvalidComponentCommandData)
				}
				seenBannerLinks[linkID] = struct{}{}
			}
		}
		seenActions := make(map[string]struct{}, len(rule.ActionOverrides))
		for _, override := range rule.ActionOverrides {
			if !validExactVocabulary(override.ActionKey, MaxOpaqueIdentifierBytes, false) ||
				!validExactVocabulary(override.Mode, MaxKindBytes, false) || !validText(override.Reason, MaxDescriptionBytes) {
				return invalid(InvalidComponentCommandData)
			}
			if _, duplicate := seenActions[override.ActionKey]; duplicate {
				return invalid(InvalidComponentCommandData)
			}
			seenActions[override.ActionKey] = struct{}{}
		}
		if rule.ReadOnly != nil && !validText(rule.ReadOnly.Reason, MaxDescriptionBytes) {
			return invalid(InvalidComponentCommandData)
		}
	}
	return nil
}

type valueBudget struct {
	nodes       int
	collections int
}

func validateNamedValues(values []NamedValue, maximum int, budget *valueBudget) error {
	if values == nil {
		return invalid(InvalidComponentFieldValue)
	}
	if len(values) > maximum || len(values) > MaxValueCollectionEntries {
		return exhausted(LimitOperationCount, min(maximum, MaxValueCollectionEntries))
	}
	previous := ""
	for index, named := range values {
		if !validOpaque(named.Name, MaxValueKeyBytes) || (index > 0 && named.Name <= previous) {
			return invalid(InvalidComponentFieldValue)
		}
		previous = named.Name
		if err := validateValue(named.Value, 1, budget); err != nil {
			return err
		}
	}
	return nil
}

func validateValue(value Value, depth int, budget *valueBudget) error {
	if budget == nil {
		return invalid(InvalidComponentFieldValue)
	}
	if depth > MaxValueDepth {
		return exhausted(LimitQueryDepth, MaxValueDepth)
	}
	budget.nodes++
	if budget.nodes > MaxValueNodes {
		return exhausted(LimitOperationCount, MaxValueNodes)
	}
	if !value.Kind.Valid() {
		return invalid(InvalidComponentFieldValue)
	}
	arms := 0
	for _, present := range []bool{value.Boolean != nil, value.String != nil, value.Decimal != nil, value.List != nil, value.Object != nil} {
		if present {
			arms++
		}
	}
	if value.Kind == ValueNull {
		if arms != 0 {
			return invalid(InvalidComponentFieldValue)
		}
		return nil
	}
	if arms != 1 {
		return invalid(InvalidComponentFieldValue)
	}
	switch value.Kind {
	case ValueBoolean:
		if value.Boolean == nil {
			return invalid(InvalidComponentFieldValue)
		}
	case ValueString:
		if value.String == nil || !validText(*value.String, MaxValueStringBytes) {
			return invalid(InvalidComponentFieldValue)
		}
	case ValueDecimal:
		if value.Decimal == nil || len(*value.Decimal) > MaxDecimalBytes || !decimalPattern.MatchString(*value.Decimal) {
			return invalid(InvalidComponentFieldValue)
		}
	case ValueList:
		if value.List == nil || *value.List == nil {
			return invalid(InvalidComponentFieldValue)
		}
		if len(*value.List) > MaxValueCollectionEntries {
			return exhausted(LimitOperationCount, MaxValueCollectionEntries)
		}
		if err := takeValueCollection(budget); err != nil {
			return err
		}
		for _, item := range *value.List {
			if err := validateValue(item, depth+1, budget); err != nil {
				return err
			}
		}
	case ValueObject:
		if value.Object == nil || *value.Object == nil {
			return invalid(InvalidComponentFieldValue)
		}
		if len(*value.Object) > MaxValueCollectionEntries {
			return exhausted(LimitOperationCount, MaxValueCollectionEntries)
		}
		if err := takeValueCollection(budget); err != nil {
			return err
		}
		previous := ""
		for index, named := range *value.Object {
			if !validOpaque(named.Name, MaxValueKeyBytes) || (index > 0 && named.Name <= previous) {
				return invalid(InvalidComponentFieldValue)
			}
			previous = named.Name
			if err := validateValue(named.Value, depth+1, budget); err != nil {
				return err
			}
		}
	}
	return nil
}

func takeValueCollection(budget *valueBudget) error {
	budget.collections++
	if budget.collections > MaxValueCollections {
		return exhausted(LimitOperationCount, MaxValueCollections)
	}
	return nil
}

func validateCommandStatusShape(status CommandStatus, operation *CommandOperation, baseRevision uint64) error {
	if !validOpaque(status.CommandID, MaxCommandIdentifierBytes) || !digestPattern.MatchString(status.RequestDigest) ||
		!status.State.Valid() || !common.ValidReceipts(status.Receipts, CapabilityName) ||
		(baseRevision != 0 && status.Receipts.CatalogRevision < baseRevision) {
		return internal()
	}
	switch status.State {
	case StateAccepted, StateInProgress:
		if status.Result != nil || status.Failure != nil || status.CatalogViewRef != nil {
			return internal()
		}
	case StateFailed:
		if status.Result != nil || status.Failure == nil || status.CatalogViewRef != nil ||
			!validateCommandFailure(*status.Failure, operation) {
			return internal()
		}
	case StateSucceeded:
		if status.Result == nil || status.Failure != nil || status.CatalogViewRef == nil ||
			ValidateCatalogViewRef(*status.CatalogViewRef) != nil ||
			status.Receipts.CatalogRevision != status.CatalogViewRef.CatalogRevision ||
			(baseRevision != 0 && status.CatalogViewRef.CatalogRevision <= baseRevision) {
			return internal()
		}
		if err := validateCommandResult(*status.Result, operation); err != nil {
			return err
		}
	}
	return nil
}

func validateCommandResult(result CommandResult, operation *CommandOperation) error {
	arms := resultArmCount(result)
	if arms != 1 {
		return internal()
	}
	if operation != nil && !matchingResultArm(*operation, result) {
		return internal()
	}
	switch {
	case result.Create != nil:
		if err := validateLayoutSnapshot(result.Create.Layout); err != nil {
			return err
		}
	case result.Update != nil:
		if err := validateLayoutSnapshot(result.Update.Layout); err != nil {
			return err
		}
	case result.Clone != nil:
		if err := validateLayoutSnapshot(result.Clone.Layout); err != nil {
			return err
		}
	case result.SetDefault != nil:
		value := result.SetDefault
		if !common.ValidObjectIdentifier(value.ObjectID) || !validIdentifier(value.LayoutID) || !validVersion(value.WorkspaceVersion) {
			return internal()
		}
	case result.Delete != nil:
		value := result.Delete
		if !validIdentifier(value.LayoutID) || !common.ValidObjectIdentifier(value.ObjectID) || !validVersion(value.WorkspaceVersion) {
			return internal()
		}
	case result.Restore != nil:
		if result.Restore.HistoryVersion == 0 {
			return internal()
		}
		if err := validateLayoutSnapshot(result.Restore.Layout); err != nil {
			return err
		}
	case result.AssignmentUpsert != nil:
		value := result.AssignmentUpsert
		if selectorCount(value.Assignment.Selectors) == 0 ||
			validateAssignmentSummary(value.Assignment, value.Assignment.ObjectID) != nil ||
			!validVersion(value.WorkspaceVersion) {
			return internal()
		}
	case result.AssignmentDelete != nil:
		value := result.AssignmentDelete
		if !validIdentifier(value.AssignmentID) || !common.ValidObjectIdentifier(value.ObjectID) || !validVersion(value.WorkspaceVersion) {
			return internal()
		}
	}
	if operation != nil && !resultMatchesOperation(*operation, result) {
		return internal()
	}
	return nil
}

// resultMatchesOperation is the relational half of the command contract. A
// result can be structurally valid and still be invalid for the accepted
// request if it names a different aggregate, assignment, history entry, or
// selector tuple, or reuses a precondition version for an effect that must
// change the covered facts.
func resultMatchesOperation(operation CommandOperation, result CommandResult) bool {
	switch {
	case operation.Create != nil && result.Create != nil:
		request := operation.Create
		actual := result.Create.Layout
		if !reflect.DeepEqual(actual.Document, request.Document) ||
			(request.ExpectedWorkspaceVersion != nil && actual.WorkspaceVersion == *request.ExpectedWorkspaceVersion) {
			return false
		}
		switch request.ListFields.Mode {
		case ChangeSet:
			if request.ListFields.Value == nil {
				return false
			}
			return equalStrings(actual.OrderedListFields, *request.ListFields.Value)
		case ChangeClear:
			return len(actual.OrderedListFields) == 0
		default:
			return true
		}
	case operation.Update != nil && result.Update != nil:
		request := operation.Update
		actual := result.Update.Layout
		return actual.Document.LayoutID == request.LayoutID &&
			matchesChange(request.Changes.Name, actual.Document.Name, "") &&
			matchesChange(request.Changes.Kind, actual.Document.Kind, "") &&
			matchesChange(request.Changes.LayoutVersion, actual.Document.LayoutVersion, uint32(0)) &&
			matchesChange(request.Changes.CompactFields, actual.Document.CompactFields, []string{}) &&
			matchesChange(request.Changes.Tabs, actual.Document.Tabs, []string{}) &&
			matchesChange(request.Changes.Sections, actual.Document.Sections, []LayoutSection{}) &&
			matchesChange(request.Changes.RelatedLists, actual.Document.RelatedLists, []RelatedList{}) &&
			matchesChange(request.Changes.RecordPanels, actual.Document.RecordPanels, []RecordPanel{}) &&
			matchesChange(request.Changes.HeaderActions, actual.Document.HeaderActions, ActionPlacement{Actions: []LayoutAction{}}) &&
			matchesChange(request.Changes.QuickActions, actual.Document.QuickActions, ActionPlacement{Actions: []LayoutAction{}}) &&
			matchesChange(request.Changes.RecordStates, actual.Document.RecordStates, []RecordStateRule{}) &&
			matchesChange(request.Changes.PrimaryHeaderActions, actual.Document.PrimaryHeaderActions, []string{}) &&
			matchesChange(request.ListFields, actual.OrderedListFields, []string{})
	case operation.Clone != nil && result.Clone != nil:
		return result.Clone.Layout.Document.LayoutID == operation.Clone.TargetLayoutID &&
			result.Clone.Layout.Document.Name == operation.Clone.TargetName &&
			result.Clone.Layout.WorkspaceVersion != operation.Clone.ExpectedWorkspaceVersion
	case operation.SetDefault != nil && result.SetDefault != nil:
		return result.SetDefault.ObjectID == operation.SetDefault.ObjectID &&
			result.SetDefault.LayoutID == operation.SetDefault.LayoutID
	case operation.Delete != nil && result.Delete != nil:
		return result.Delete.LayoutID == operation.Delete.LayoutID &&
			result.Delete.WorkspaceVersion != operation.Delete.ExpectedWorkspaceVersion
	case operation.Restore != nil && result.Restore != nil:
		return result.Restore.Layout.Document.LayoutID == operation.Restore.LayoutID &&
			result.Restore.HistoryVersion == operation.Restore.HistoryVersion
	case operation.AssignmentUpsert != nil && result.AssignmentUpsert != nil:
		return result.AssignmentUpsert.Assignment.ObjectID == operation.AssignmentUpsert.ObjectID &&
			result.AssignmentUpsert.Assignment.LayoutID == operation.AssignmentUpsert.LayoutID &&
			equalSelectors(result.AssignmentUpsert.Assignment.Selectors, operation.AssignmentUpsert.Selectors)
	case operation.AssignmentDelete != nil && result.AssignmentDelete != nil:
		return result.AssignmentDelete.AssignmentID == operation.AssignmentDelete.AssignmentID &&
			result.AssignmentDelete.WorkspaceVersion != operation.AssignmentDelete.ExpectedWorkspaceVersion
	default:
		return false
	}
}

func matchesChange[T any](change Change[T], actual, cleared T) bool {
	switch change.Mode {
	case ChangePreserve:
		return true
	case ChangeSet:
		return change.Value != nil && reflect.DeepEqual(actual, *change.Value)
	case ChangeClear:
		return change.Value == nil && reflect.DeepEqual(actual, cleared)
	default:
		return false
	}
}

func equalSelectors(left, right AssignmentSelectors) bool {
	return equalOptionalString(left.ProfileID, right.ProfileID) &&
		equalOptionalString(left.RoleID, right.RoleID) &&
		equalOptionalString(left.RecordTypeID, right.RecordTypeID)
}

func equalOptionalString(left, right *string) bool {
	return left == nil && right == nil || left != nil && right != nil && *left == *right
}

func equalStrings(left, right []string) bool {
	if len(left) != len(right) {
		return false
	}
	for index := range left {
		if left[index] != right[index] {
			return false
		}
	}
	return true
}

func resultArmCount(result CommandResult) int {
	count := 0
	for _, present := range []bool{
		result.Create != nil, result.Update != nil, result.Clone != nil, result.SetDefault != nil,
		result.Delete != nil, result.Restore != nil, result.AssignmentUpsert != nil, result.AssignmentDelete != nil,
	} {
		if present {
			count++
		}
	}
	return count
}

func matchingResultArm(operation CommandOperation, result CommandResult) bool {
	return operation.Create != nil && result.Create != nil ||
		operation.Update != nil && result.Update != nil ||
		operation.Clone != nil && result.Clone != nil ||
		operation.SetDefault != nil && result.SetDefault != nil ||
		operation.Delete != nil && result.Delete != nil ||
		operation.Restore != nil && result.Restore != nil ||
		operation.AssignmentUpsert != nil && result.AssignmentUpsert != nil ||
		operation.AssignmentDelete != nil && result.AssignmentDelete != nil
}

func validateLayoutSnapshot(snapshot LayoutSnapshot) error {
	if err := ValidateLayoutDocument(snapshot.Document); err != nil {
		return internal()
	}
	if err := validateFieldIDs(snapshot.OrderedListFields, MaxListFields, false); err != nil {
		return internal()
	}
	if !validVersion(snapshot.ResourceVersion) || !validVersion(snapshot.AggregateVersion) ||
		!validVersion(snapshot.WorkspaceVersion) {
		return internal()
	}
	return nil
}

func validateReadView(requested *CatalogViewRef, returned CatalogViewRef, receipts Receipts) error {
	if ValidateCatalogViewRef(returned) != nil || !common.ValidReceipts(receipts, CapabilityName) ||
		receipts.CatalogRevision != returned.CatalogRevision {
		return internal()
	}
	if requested != nil && *requested != returned {
		return internal()
	}
	return nil
}

func validateLayoutSummary(layout LayoutSummary, objectID string) error {
	if !validIdentifier(layout.LayoutID) || layout.ObjectID != objectID ||
		!validExactVocabulary(layout.Name, MaxNameBytes, false) || !validExactVocabulary(layout.Kind, MaxKindBytes, true) ||
		!validVersion(layout.ResourceVersion) || !validTimestamp(layout.CreatedAt) ||
		!validTimestamp(layout.LastModifiedAt) {
		return internal()
	}
	return nil
}

func validateAssignmentSummary(assignment LayoutAssignmentSummary, objectID string) error {
	if !validIdentifier(assignment.AssignmentID) || assignment.ObjectID != objectID ||
		!common.ValidObjectIdentifier(assignment.ObjectID) || !validIdentifier(assignment.LayoutID) ||
		!validSelectors(assignment.Selectors) || assignment.Specificity != uint32(selectorCount(assignment.Selectors)) ||
		!validVersion(assignment.ResourceVersion) || !validTimestamp(assignment.CreatedAt) ||
		!validTimestamp(assignment.LastModifiedAt) || !validOptionalOpaque(assignment.CreatedByID, MaxOpaqueIdentifierBytes) {
		return internal()
	}
	return nil
}

func validateLifecycle(lifecycle LayoutLifecycleState) error {
	if !validExactVocabulary(lifecycle.ProvenanceKind, MaxKindBytes, false) || !validTimestamp(lifecycle.CreatedAt) ||
		!validTimestamp(lifecycle.LastModifiedAt) {
		return internal()
	}
	return nil
}

func validateCommandFailure(failure CommandFailure, operation *CommandOperation) bool {
	return ValidCommandFailureCategory(failure.Category) &&
		validCommandFailureMessage(failure.Message) &&
		(failure.FieldReference == "" || validCommandFailureFieldReference(failure.FieldReference, operation))
}

// validCommandFailureMessage validates durable caller-facing text, not
// authored Layout content. Failure messages are deliberately single-line and
// reject invisible or direction-changing characters so their persisted value
// cannot acquire a different meaning in logs, UI, or protocol projections.
func validCommandFailureMessage(value string) bool {
	if value == "" || len(value) > MaxFailureMessageBytes || !utf8.ValidString(value) ||
		strings.TrimSpace(value) != value {
		return false
	}
	for _, character := range value {
		if unicode.IsControl(character) || unicode.Is(unicode.Cf, character) ||
			unicode.Is(unicode.Zl, character) || unicode.Is(unicode.Zp, character) {
			return false
		}
	}
	return true
}

func validateFieldIDListChange(change FieldIDListChange) error {
	return validateChange(&change, true, func(value []string) error {
		return validateFieldIDs(value, MaxListFields, false)
	})
}

func validateChange[T any](change *Change[T], allowClear bool, validateSet func(T) error) error {
	if change == nil || !change.Mode.Valid() || (change.Mode == ChangeClear && !allowClear) {
		return invalid(InvalidComponentCommandData)
	}
	switch change.Mode {
	case ChangePreserve, ChangeClear:
		if change.Value != nil {
			return invalid(InvalidComponentCommandData)
		}
	case ChangeSet:
		if change.Value == nil {
			return invalid(InvalidComponentCommandData)
		}
		return validateSet(*change.Value)
	}
	return nil
}

func changesAnything(changes LayoutDocumentChanges) bool {
	for _, mode := range []ChangeMode{
		changes.Name.Mode, changes.Kind.Mode, changes.LayoutVersion.Mode, changes.CompactFields.Mode,
		changes.Tabs.Mode, changes.Sections.Mode, changes.RelatedLists.Mode, changes.RecordPanels.Mode,
		changes.HeaderActions.Mode, changes.QuickActions.Mode, changes.RecordStates.Mode,
		changes.PrimaryHeaderActions.Mode,
	} {
		if mode != ChangePreserve {
			return true
		}
	}
	return false
}

func validateFieldIDs(values []string, maximum int, requireNonEmpty bool) error {
	if values == nil || (requireNonEmpty && len(values) == 0) {
		return invalid(InvalidComponentFieldValue)
	}
	if len(values) > maximum {
		return exhausted(LimitFieldCount, maximum)
	}
	seen := make(map[string]struct{}, len(values))
	for _, value := range values {
		if !common.ValidFieldIdentifier(value) {
			return invalid(InvalidComponentFieldValue)
		}
		key := strings.ToLower(value)
		if _, duplicate := seen[key]; duplicate {
			return invalid(InvalidComponentFieldValue)
		}
		seen[key] = struct{}{}
	}
	return nil
}

func validateOpaqueList(values []string, maximum int) error {
	if values == nil {
		return invalid(InvalidComponentCommandData)
	}
	if len(values) > maximum {
		return exhausted(LimitOperationCount, maximum)
	}
	seen := make(map[string]struct{}, len(values))
	for _, value := range values {
		if !validIdentifier(value) {
			return invalid(InvalidComponentCommandData)
		}
		if _, duplicate := seen[value]; duplicate {
			return invalid(InvalidComponentCommandData)
		}
		seen[value] = struct{}{}
	}
	return nil
}

func validSelectors(selectors AssignmentSelectors) bool {
	return validOptionalOpaque(selectors.ProfileID, MaxOpaqueIdentifierBytes) &&
		validOptionalOpaque(selectors.RoleID, MaxOpaqueIdentifierBytes) &&
		validOptionalOpaque(selectors.RecordTypeID, MaxOpaqueIdentifierBytes)
}

func selectorCount(selectors AssignmentSelectors) int {
	count := 0
	for _, present := range []bool{selectors.ProfileID != nil, selectors.RoleID != nil, selectors.RecordTypeID != nil} {
		if present {
			count++
		}
	}
	return count
}

func selectorTuple(selectors AssignmentSelectors) string {
	value := func(pointer *string) string {
		if pointer == nil {
			return "-"
		}
		return "+" + routingIdentityKey(*pointer)
	}
	return value(selectors.ProfileID) + "\x00" + value(selectors.RoleID) + "\x00" + value(selectors.RecordTypeID)
}

// Routing identities follow the Core Catalog compiler's physical-key rules:
// comparisons are case-insensitive while returned facts retain their authored
// bytes. The raw tie-breaker makes ordering deterministic before duplicate
// folded identities are rejected.
func routingIdentityKey(value string) string { return strings.ToLower(value) }

func routingIdentityLess(left, right string) bool {
	leftKey, rightKey := routingIdentityKey(left), routingIdentityKey(right)
	if leftKey == rightKey {
		return left < right
	}
	return leftKey < rightKey
}

func validIdentifier(value string) bool {
	return validOpaque(value, MaxOpaqueIdentifierBytes)
}

func validVersion(value string) bool {
	return validOpaque(value, MaxVersionBytes)
}

// validOpaque validates durable protocol identities. Unlike authored text it
// is single-line, has no leading/trailing whitespace, and rejects every
// Unicode control, format, line-separator, or paragraph-separator character.
// These characters are invisible, direction-changing, or line-breaking and
// therefore cannot safely identify durable commands, resources, versions,
// selectors, or extension keys.
func validOpaque(value string, maximum int) bool {
	if value == "" || strings.TrimSpace(value) != value || !validExactVocabulary(value, maximum, false) {
		return false
	}
	for _, character := range value {
		if unicode.Is(unicode.Cf, character) || unicode.Is(unicode.Zl, character) || unicode.Is(unicode.Zp, character) {
			return false
		}
	}
	return true
}

// validRequiredAuthoredText preserves authored bytes while requiring content.
// Newlines and tabs are valid in labels, expressions, descriptions, and other
// actual text fields.
func validRequiredAuthoredText(value string, maximum int) bool {
	return value != "" && validText(value, maximum)
}

func validOptionalOpaque(value *string, maximum int) bool {
	return value == nil || validOpaque(*value, maximum)
}

// validExactVocabulary bounds an app-owned or stored scalar without changing
// its bytes. Surrounding whitespace is meaningful; control characters are
// never vocabulary. allowEmpty is explicit because several Core facts retain
// a meaningful present-empty value.
func validExactVocabulary(value string, maximum int, allowEmpty bool) bool {
	if (!allowEmpty && value == "") || len(value) > maximum || !utf8.ValidString(value) {
		return false
	}
	for _, character := range value {
		if unicode.IsControl(character) {
			return false
		}
	}
	return true
}

func validOptionalExactVocabulary(value *string, maximum int) bool {
	return value == nil || validExactVocabulary(*value, maximum, true)
}

func validOptionalText(value *string, maximum int) bool {
	return value == nil || validText(*value, maximum)
}

func validOptionalObjectID(value *string) bool {
	return value == nil || *value == "" || common.ValidObjectIdentifier(*value)
}

// validFieldReference accepts the complete physical relationship path stored
// by Core (for example OwnerId.Name) while keeping each segment under the
// shared physical field grammar. L0 assigns no product meaning to segments.
func validFieldReference(value string) bool {
	if len(value) == 0 || len(value) > MaxFieldReferenceBytes {
		return false
	}
	for _, segment := range strings.Split(value, ".") {
		if !common.ValidFieldIdentifier(segment) {
			return false
		}
	}
	return true
}

func validText(value string, maximum int) bool {
	if len(value) > maximum || !utf8.ValidString(value) {
		return false
	}
	for _, character := range value {
		if unicode.IsControl(character) && character != '\n' && character != '\r' && character != '\t' {
			return false
		}
	}
	return true
}

func validTimestamp(value string) bool {
	if value == "" {
		return true
	}
	parsed, err := time.Parse(time.RFC3339Nano, value)
	return err == nil && parsed.Location() == time.UTC && strings.HasSuffix(value, "Z")
}

func invalid(component InvalidComponent) *Failure {
	return &Failure{Category: FailureInvalidRequest, InvalidComponent: component}
}

func exhausted(kind LimitKind, maximum int) *Failure {
	return &Failure{Category: FailureResourceExhausted, LimitKind: kind, Maximum: uint64(maximum)}
}

func internal() *Failure { return &Failure{Category: FailureInternal} }
