package configwire

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	SignatureVerifierKind = "signature_verifier"
	EnvelopeParserKind    = "envelope_parser"
)

type SignatureSchemeConfig struct {
	Scheme                             string
	SigningTemplate                    string
	TimestampHeader                    string
	SignatureHeader                    string
	SignatureEncoding                  string
	VerifiedIngressReceipt             bool
	DeliveryIdentityBodyPath           string
	DeliveryIdentityCollectionPath     string
	DeliveryIdentityCollectionKeyPaths []string
	ReceiptHeaderAllowlist             []string
	SkewWindow                         time.Duration
	SecretName                         string
	MethodHeader                       string
	URIHeader                          string
}

type SignatureSchemeConfigSet struct {
	Default         *SignatureSchemeConfig
	EndpointConfigs map[string]SignatureSchemeConfig
}

type EnvelopeMappingConfig struct {
	EventArrayPath        string
	EventObjectPath       string
	InboundProcessingMode InboundProcessingMode
	Rules                 []EnvelopeMappingRule
}

type EnvelopeMappingConfigSet struct {
	Default         *EnvelopeMappingConfig
	EndpointConfigs map[string]EnvelopeMappingConfig
}

// EnvelopeMappingConfigCompositeSidecars carries index-aligned composite
// identity metadata without changing the positional layout of the legacy
// exported config structs.
type EnvelopeMappingConfigCompositeSidecars struct {
	Default         []*IdempotencyKeyComposite
	EndpointConfigs map[string][]*IdempotencyKeyComposite
}

type EnvelopeMappingRule struct {
	MatchPath            string
	MatchEquals          string
	MatchAll             []MatchCondition
	EventTypeFromPath    string
	EventTypeLiteral     string
	NamespaceFromCtx     bool
	ObjectKind           string
	SourceIDPath         string
	SourceLaneObjectKind string
	SourceLaneIDPath     string
	IdempotencyKeyPath   string
	PayloadProjection    []PayloadField
}

type IdempotencyKeyComposite struct {
	Version  string
	Encoding string
	Parts    []IdempotencyKeyPart
}

type IdempotencyKeyPart struct {
	Path string
}

type MatchCondition struct {
	Path   string
	Equals string
}

type ruleSelector struct{ path, equals string }

type PayloadField struct {
	Name string
	Path string
	Mode string
}

type InboundProcessingMode string

const (
	InboundProcessingModeEnrichmentRequired InboundProcessingMode = "enrichment_required"
	InboundProcessingModePayloadOnlyAllowed InboundProcessingMode = "payload_only_allowed"
)

func canonicalizePayloadFieldMode(raw string) (string, error) {
	mode := strings.ToLower(strings.TrimSpace(raw))
	switch mode {
	case "", "scalar_string", "scalar_string_array":
		return mode, nil
	default:
		return "", fmt.Errorf("PayloadField.Mode %q is not one of {\"scalar_string\", \"scalar_string_array\"}", raw)
	}
}

type signatureSchemeConfigWire struct {
	Scheme                             string   `json:"scheme"`
	SigningTemplate                    string   `json:"signingTemplate"`
	TimestampHeader                    string   `json:"timestampHeader,omitempty"`
	SignatureHeader                    string   `json:"signatureHeader"`
	SignatureEncoding                  string   `json:"signatureEncoding,omitempty"`
	VerifiedIngressReceipt             bool     `json:"verifiedIngressReceipt,omitempty"`
	DeliveryIdentityBodyPath           string   `json:"deliveryIdentityBodyPath,omitempty"`
	DeliveryIdentityCollectionPath     string   `json:"deliveryIdentityCollectionPath,omitempty"`
	DeliveryIdentityCollectionKeyPaths []string `json:"deliveryIdentityCollectionKeyPaths,omitempty"`
	ReceiptHeaderAllowlist             []string `json:"receiptHeaderAllowlist,omitempty"`
	SkewWindow                         string   `json:"skewWindow,omitempty"`
	SecretName                         string   `json:"secretName,omitempty"`
	MethodHeader                       string   `json:"methodHeader,omitempty"`
	URIHeader                          string   `json:"uriHeader,omitempty"`
}

type endpointConfigSetWire struct {
	EndpointConfigs map[string]json.RawMessage `json:"endpointConfigs"`
}

type envelopeMappingConfigWire struct {
	EventArrayPath        string                    `json:"eventArrayPath,omitempty"`
	EventObjectPath       string                    `json:"eventObjectPath,omitempty"`
	InboundProcessingMode string                    `json:"inboundProcessingMode,omitempty"`
	Rules                 []envelopeMappingRuleWire `json:"rules"`
}

type envelopeMappingRuleWire struct {
	MatchPath                    string                       `json:"matchPath,omitempty"`
	MatchEquals                  string                       `json:"matchEquals,omitempty"`
	MatchAll                     *[]matchConditionWire        `json:"matchAll,omitempty"`
	EventTypeFromPath            string                       `json:"eventTypeFromPath,omitempty"`
	EventTypeLiteral             string                       `json:"eventTypeLiteral,omitempty"`
	NamespaceFromCtx             bool                         `json:"namespaceFromCtx,omitempty"`
	ObjectKind                   string                       `json:"objectKind"`
	SourceIDPath                 string                       `json:"sourceIdPath"`
	SourceLaneObjectKind         string                       `json:"sourceLaneObjectKind,omitempty"`
	SourceLaneIDPath             string                       `json:"sourceLaneIdPath,omitempty"`
	TargetMutationLaneObjectKind string                       `json:"targetMutationLaneObjectKind,omitempty"`
	TargetMutationLaneIDPath     string                       `json:"targetMutationLaneIdPath,omitempty"`
	IdempotencyKeyPath           string                       `json:"idempotencyKeyPath,omitempty"`
	IdempotencyKeyComposite      *idempotencyKeyCompositeWire `json:"idempotencyKeyComposite,omitempty"`
	PayloadProjection            []payloadFieldWire           `json:"payloadProjection,omitempty"`
	matchAllNull                 bool
	idempotencyKeyCompositeNull  bool
}

func (r *envelopeMappingRuleWire) UnmarshalJSON(data []byte) error {
	type alias envelopeMappingRuleWire
	var out alias
	if err := decodeStrict(data, &out); err != nil {
		return err
	}
	var raw map[string]json.RawMessage
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}
	if matchAll, ok := raw["matchAll"]; ok && bytes.Equal(bytes.TrimSpace(matchAll), []byte("null")) {
		out.matchAllNull = true
	}
	if composite, ok := raw["idempotencyKeyComposite"]; ok && bytes.Equal(bytes.TrimSpace(composite), []byte("null")) {
		out.idempotencyKeyCompositeNull = true
	}
	*r = envelopeMappingRuleWire(out)
	return nil
}

type idempotencyKeyCompositeWire struct {
	Version  string                   `json:"version"`
	Encoding string                   `json:"encoding"`
	Parts    []idempotencyKeyPartWire `json:"parts"`
}

type idempotencyKeyPartWire struct {
	Path string `json:"path"`
}

type matchConditionWire struct {
	Path   string `json:"path"`
	Equals string `json:"equals,omitempty"`
}

type payloadFieldWire struct {
	Name string `json:"name"`
	Path string `json:"path"`
	Mode string `json:"mode,omitempty"`
}

var (
	templateTokenRE       = regexp.MustCompile(`\{[^}]+\}`)
	allowedTemplateTokens = map[string]bool{
		"{Method}":     true,
		"{RequestURI}": true,
		"{RequestURL}": true,
		"{RawBody}":    true,
		"{Timestamp}":  true,
	}
)

func ValidateCapabilityConfig(namespace, kind string, raw []byte) (string, error) {
	switch strings.TrimSpace(kind) {
	case SignatureVerifierKind:
		_, canonical, err := DecodeSignatureSchemeConfigSet(raw)
		return canonical, err
	case EnvelopeParserKind:
		_, _, canonical, err := DecodeEnvelopeMappingConfigSetWithComposites(strings.TrimSpace(namespace), raw)
		return canonical, err
	default:
		return "", fmt.Errorf("unknown connector inbound handler config capability kind %q", kind)
	}
}

// EndpointConfigNames returns the canonical webhook-secret endpoint names when a
// handler config uses endpoint-specific sub-configs. The boolean is false for
// legacy/shared configs with no endpointConfigs wrapper.
func EndpointConfigNames(raw []byte) ([]string, bool, error) {
	if !hasEndpointConfigs(raw) {
		return nil, false, nil
	}
	rawByEndpoint, err := decodeEndpointConfigMap(raw)
	if err != nil {
		return nil, true, err
	}
	return sortedRawEndpointNames(rawByEndpoint), true, nil
}

func DecodeSignatureSchemeConfigSet(raw []byte) (SignatureSchemeConfigSet, string, error) {
	if !hasEndpointConfigs(raw) {
		cfg, canonical, err := DecodeSignatureSchemeConfig(raw)
		if err != nil {
			return SignatureSchemeConfigSet{}, "", err
		}
		if cfg.SecretName == "" {
			return SignatureSchemeConfigSet{}, "", fmt.Errorf("validate signature_verifier config: SecretName is empty")
		}
		return SignatureSchemeConfigSet{Default: &cfg}, canonical, nil
	}

	rawByEndpoint, err := decodeEndpointConfigMap(raw)
	if err != nil {
		return SignatureSchemeConfigSet{}, "", fmt.Errorf("parse signature_verifier endpointConfigs: %w", err)
	}
	configs := make(map[string]SignatureSchemeConfig, len(rawByEndpoint))
	canonicalConfigs := make(map[string]json.RawMessage, len(rawByEndpoint))
	for _, endpoint := range sortedRawEndpointNames(rawByEndpoint) {
		cfg, canonical, err := DecodeSignatureSchemeConfig(rawByEndpoint[endpoint])
		if err != nil {
			return SignatureSchemeConfigSet{}, "", fmt.Errorf("endpointConfigs[%q]: %w", endpoint, err)
		}
		if got := canonicalEndpointName(cfg.SecretName); got != "" && got != endpoint {
			return SignatureSchemeConfigSet{}, "", fmt.Errorf("endpointConfigs[%q]: signature_verifier secretName %q must match endpointConfigs key", endpoint, cfg.SecretName)
		} else if got != "" {
			cfg.SecretName = got
		}
		if cfg.SecretName == "" {
			cfg.SecretName = endpoint
		}
		configs[endpoint] = cfg
		canonicalConfigs[endpoint] = json.RawMessage(canonical)
	}
	canonicalBytes, err := json.Marshal(endpointConfigSetWire{EndpointConfigs: canonicalConfigs})
	if err != nil {
		return SignatureSchemeConfigSet{}, "", fmt.Errorf("canonicalize signature_verifier endpointConfigs: %w", err)
	}
	return SignatureSchemeConfigSet{EndpointConfigs: configs}, string(canonicalBytes), nil
}

func DecodeSignatureSchemeConfig(raw []byte) (SignatureSchemeConfig, string, error) {
	var wire signatureSchemeConfigWire
	if err := decodeStrict(raw, &wire); err != nil {
		return SignatureSchemeConfig{}, "", fmt.Errorf("parse signature_verifier config: %w", err)
	}
	var skewWindow time.Duration
	if rawSkew := strings.TrimSpace(wire.SkewWindow); rawSkew != "" {
		var err error
		skewWindow, err = time.ParseDuration(rawSkew)
		if err != nil {
			return SignatureSchemeConfig{}, "", fmt.Errorf("parse signature_verifier config skewWindow %q: %w", wire.SkewWindow, err)
		}
	}
	cfg := SignatureSchemeConfig{
		Scheme:                 wire.Scheme,
		SigningTemplate:        wire.SigningTemplate,
		TimestampHeader:        wire.TimestampHeader,
		SignatureHeader:        wire.SignatureHeader,
		SignatureEncoding:      wire.SignatureEncoding,
		VerifiedIngressReceipt: wire.VerifiedIngressReceipt,
		SkewWindow:             skewWindow,
		SecretName:             wire.SecretName,
		MethodHeader:           wire.MethodHeader,
		URIHeader:              wire.URIHeader,
	}
	if wire.VerifiedIngressReceipt {
		cfg.DeliveryIdentityBodyPath = wire.DeliveryIdentityBodyPath
		cfg.DeliveryIdentityCollectionPath = wire.DeliveryIdentityCollectionPath
		cfg.DeliveryIdentityCollectionKeyPaths = append([]string(nil), wire.DeliveryIdentityCollectionKeyPaths...)
		cfg.ReceiptHeaderAllowlist = append([]string(nil), wire.ReceiptHeaderAllowlist...)
	}
	if err := validateSignatureSchemeConfig(&cfg); err != nil {
		return SignatureSchemeConfig{}, "", fmt.Errorf("validate signature_verifier config: %w", err)
	}
	canonicalBytes, err := json.Marshal(signatureSchemeConfigWire{
		Scheme:                             cfg.Scheme,
		SigningTemplate:                    cfg.SigningTemplate,
		TimestampHeader:                    cfg.TimestampHeader,
		SignatureHeader:                    cfg.SignatureHeader,
		SignatureEncoding:                  cfg.SignatureEncoding,
		VerifiedIngressReceipt:             cfg.VerifiedIngressReceipt,
		DeliveryIdentityBodyPath:           cfg.DeliveryIdentityBodyPath,
		DeliveryIdentityCollectionPath:     cfg.DeliveryIdentityCollectionPath,
		DeliveryIdentityCollectionKeyPaths: cfg.DeliveryIdentityCollectionKeyPaths,
		ReceiptHeaderAllowlist:             cfg.ReceiptHeaderAllowlist,
		SkewWindow:                         strings.TrimSpace(wire.SkewWindow),
		SecretName:                         cfg.SecretName,
		MethodHeader:                       cfg.MethodHeader,
		URIHeader:                          cfg.URIHeader,
	})
	if err != nil {
		return SignatureSchemeConfig{}, "", fmt.Errorf("canonicalize signature_verifier config: %w", err)
	}
	return cfg, string(canonicalBytes), nil
}

func DecodeEnvelopeMappingConfigSet(namespace string, raw []byte) (EnvelopeMappingConfigSet, string, error) {
	cfgSet, sidecars, canonical, err := DecodeEnvelopeMappingConfigSetWithComposites(namespace, raw)
	if err == nil && (len(sidecars.Default) != 0 || len(sidecars.EndpointConfigs) != 0) {
		return EnvelopeMappingConfigSet{}, "", fmt.Errorf("composite idempotency metadata requires DecodeEnvelopeMappingConfigSetWithComposites")
	}
	return cfgSet, canonical, err
}

func DecodeEnvelopeMappingConfigSetWithComposites(namespace string, raw []byte) (EnvelopeMappingConfigSet, EnvelopeMappingConfigCompositeSidecars, string, error) {
	if !hasEndpointConfigs(raw) {
		cfg, composites, canonical, err := DecodeEnvelopeMappingConfigWithComposites(namespace, raw)
		if err != nil {
			return EnvelopeMappingConfigSet{}, EnvelopeMappingConfigCompositeSidecars{}, "", err
		}
		return EnvelopeMappingConfigSet{Default: &cfg}, EnvelopeMappingConfigCompositeSidecars{Default: composites}, canonical, nil
	}

	rawByEndpoint, err := decodeEndpointConfigMap(raw)
	if err != nil {
		return EnvelopeMappingConfigSet{}, EnvelopeMappingConfigCompositeSidecars{}, "", fmt.Errorf("parse envelope_parser endpointConfigs: %w", err)
	}
	configs := make(map[string]EnvelopeMappingConfig, len(rawByEndpoint))
	var compositesByEndpoint map[string][]*IdempotencyKeyComposite
	canonicalConfigs := make(map[string]json.RawMessage, len(rawByEndpoint))
	for _, endpoint := range sortedRawEndpointNames(rawByEndpoint) {
		cfg, composites, canonical, err := DecodeEnvelopeMappingConfigWithComposites(namespace, rawByEndpoint[endpoint])
		if err != nil {
			return EnvelopeMappingConfigSet{}, EnvelopeMappingConfigCompositeSidecars{}, "", fmt.Errorf("endpointConfigs[%q]: %w", endpoint, err)
		}
		configs[endpoint] = cfg
		if len(composites) != 0 {
			if compositesByEndpoint == nil {
				compositesByEndpoint = make(map[string][]*IdempotencyKeyComposite)
			}
			compositesByEndpoint[endpoint] = composites
		}
		canonicalConfigs[endpoint] = json.RawMessage(canonical)
	}
	if err := validateEndpointEnvelopeMappingContracts(configs); err != nil {
		return EnvelopeMappingConfigSet{}, EnvelopeMappingConfigCompositeSidecars{}, "", err
	}
	canonicalBytes, err := json.Marshal(endpointConfigSetWire{EndpointConfigs: canonicalConfigs})
	if err != nil {
		return EnvelopeMappingConfigSet{}, EnvelopeMappingConfigCompositeSidecars{}, "", fmt.Errorf("canonicalize envelope_parser endpointConfigs: %w", err)
	}
	return EnvelopeMappingConfigSet{EndpointConfigs: configs}, EnvelopeMappingConfigCompositeSidecars{EndpointConfigs: compositesByEndpoint}, string(canonicalBytes), nil
}

func DecodeEnvelopeMappingConfig(namespace string, raw []byte) (EnvelopeMappingConfig, string, error) {
	cfg, composites, canonical, err := DecodeEnvelopeMappingConfigWithComposites(namespace, raw)
	if err == nil && len(composites) != 0 {
		return EnvelopeMappingConfig{}, "", fmt.Errorf("composite idempotency metadata requires DecodeEnvelopeMappingConfigWithComposites")
	}
	return cfg, canonical, err
}

func DecodeEnvelopeMappingConfigWithComposites(namespace string, raw []byte) (EnvelopeMappingConfig, []*IdempotencyKeyComposite, string, error) {
	if strings.TrimSpace(namespace) == "" {
		return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("validate envelope_parser config: namespace is empty")
	}
	var wire envelopeMappingConfigWire
	if err := decodeStrict(raw, &wire); err != nil {
		return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("parse envelope_parser config: %w", err)
	}
	cfg := EnvelopeMappingConfig{
		EventArrayPath:        wire.EventArrayPath,
		EventObjectPath:       wire.EventObjectPath,
		InboundProcessingMode: InboundProcessingMode(wire.InboundProcessingMode),
		Rules:                 make([]EnvelopeMappingRule, 0, len(wire.Rules)),
	}
	ruleComposites := make([]*IdempotencyKeyComposite, len(wire.Rules))
	hasComposite := false
	for _, rule := range wire.Rules {
		if rule.matchAllNull {
			return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("validate envelope_parser config: rule %d: matchAll must contain at least one condition when present", len(cfg.Rules))
		}
		if rule.idempotencyKeyCompositeNull {
			return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("validate envelope_parser config: rule %d: idempotencyKeyComposite must not be null", len(cfg.Rules))
		}
		converted := EnvelopeMappingRule{
			MatchPath:            rule.MatchPath,
			MatchEquals:          rule.MatchEquals,
			EventTypeFromPath:    rule.EventTypeFromPath,
			EventTypeLiteral:     rule.EventTypeLiteral,
			NamespaceFromCtx:     rule.NamespaceFromCtx,
			ObjectKind:           rule.ObjectKind,
			SourceIDPath:         rule.SourceIDPath,
			SourceLaneObjectKind: rule.SourceLaneObjectKind,
			SourceLaneIDPath:     rule.SourceLaneIDPath,
			IdempotencyKeyPath:   rule.IdempotencyKeyPath,
			PayloadProjection:    make([]PayloadField, 0, len(rule.PayloadProjection)),
		}
		hasSourceLaneObjectKind := strings.TrimSpace(rule.SourceLaneObjectKind) != ""
		hasSourceLaneIDPath := strings.TrimSpace(rule.SourceLaneIDPath) != ""
		if hasSourceLaneObjectKind != hasSourceLaneIDPath {
			return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("validate envelope_parser config: rule %d: SourceLaneObjectKind and SourceLaneIDPath must be configured together", len(cfg.Rules))
		}
		hasTargetMutationLaneObjectKind := strings.TrimSpace(rule.TargetMutationLaneObjectKind) != ""
		hasTargetMutationLaneIDPath := strings.TrimSpace(rule.TargetMutationLaneIDPath) != ""
		if hasTargetMutationLaneObjectKind != hasTargetMutationLaneIDPath {
			return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("validate envelope_parser config: rule %d: TargetMutationLaneObjectKind and TargetMutationLaneIDPath must be configured together", len(cfg.Rules))
		}
		if (hasSourceLaneObjectKind || hasSourceLaneIDPath) && (hasTargetMutationLaneObjectKind || hasTargetMutationLaneIDPath) {
			return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("validate envelope_parser config: rule %d: configure either SourceLane* override or TargetMutationLane*, not both", len(cfg.Rules))
		}
		if hasTargetMutationLaneObjectKind {
			converted.SourceLaneObjectKind = rule.TargetMutationLaneObjectKind
			converted.SourceLaneIDPath = rule.TargetMutationLaneIDPath
		}
		if rule.MatchAll != nil {
			converted.MatchAll = make([]MatchCondition, 0, len(*rule.MatchAll))
			for _, cond := range *rule.MatchAll {
				converted.MatchAll = append(converted.MatchAll, MatchCondition{
					Path:   cond.Path,
					Equals: cond.Equals,
				})
			}
		}
		if rule.IdempotencyKeyComposite != nil {
			composite := &IdempotencyKeyComposite{
				Version:  rule.IdempotencyKeyComposite.Version,
				Encoding: rule.IdempotencyKeyComposite.Encoding,
				Parts:    make([]IdempotencyKeyPart, 0, len(rule.IdempotencyKeyComposite.Parts)),
			}
			for _, part := range rule.IdempotencyKeyComposite.Parts {
				composite.Parts = append(composite.Parts, IdempotencyKeyPart{Path: part.Path})
			}
			ruleComposites[len(cfg.Rules)] = composite
			hasComposite = true
		}
		for _, field := range rule.PayloadProjection {
			converted.PayloadProjection = append(converted.PayloadProjection, PayloadField{
				Name: field.Name,
				Path: field.Path,
				Mode: field.Mode,
			})
		}
		cfg.Rules = append(cfg.Rules, converted)
	}
	if !hasComposite {
		ruleComposites = nil
	}
	if err := validateEnvelopeMappingConfig(&cfg, ruleComposites); err != nil {
		return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("validate envelope_parser config: %w", err)
	}
	canonicalWire := envelopeMappingConfigToWire(cfg, ruleComposites)
	for i := range canonicalWire.Rules {
		if i >= len(wire.Rules) {
			break
		}
		if strings.TrimSpace(wire.Rules[i].TargetMutationLaneObjectKind) == "" {
			continue
		}
		canonicalWire.Rules[i].SourceLaneObjectKind = ""
		canonicalWire.Rules[i].SourceLaneIDPath = ""
		canonicalWire.Rules[i].TargetMutationLaneObjectKind = strings.TrimSpace(wire.Rules[i].TargetMutationLaneObjectKind)
		canonicalWire.Rules[i].TargetMutationLaneIDPath = wire.Rules[i].TargetMutationLaneIDPath
	}
	canonicalBytes, err := json.Marshal(canonicalWire)
	if err != nil {
		return EnvelopeMappingConfig{}, nil, "", fmt.Errorf("canonicalize envelope_parser config: %w", err)
	}
	return cfg, ruleComposites, string(canonicalBytes), nil
}

func hasEndpointConfigs(raw []byte) bool {
	var probe map[string]json.RawMessage
	if err := json.Unmarshal(bytes.TrimSpace(raw), &probe); err != nil {
		return false
	}
	_, ok := probe["endpointConfigs"]
	return ok
}

func decodeEndpointConfigMap(raw []byte) (map[string]json.RawMessage, error) {
	var wire endpointConfigSetWire
	if err := decodeStrict(raw, &wire); err != nil {
		return nil, err
	}
	if len(wire.EndpointConfigs) == 0 {
		return nil, fmt.Errorf("endpointConfigs must contain at least one endpoint")
	}
	out := make(map[string]json.RawMessage, len(wire.EndpointConfigs))
	for rawEndpoint, cfg := range wire.EndpointConfigs {
		endpoint := canonicalEndpointName(rawEndpoint)
		if endpoint == "" {
			return nil, fmt.Errorf("endpointConfigs key %q is empty after trim/lowercase", rawEndpoint)
		}
		if _, exists := out[endpoint]; exists {
			return nil, fmt.Errorf("endpointConfigs contains duplicate endpoint %q after canonicalization", endpoint)
		}
		if len(bytes.TrimSpace(cfg)) == 0 {
			return nil, fmt.Errorf("endpointConfigs[%q] config JSON is required", endpoint)
		}
		out[endpoint] = append(json.RawMessage(nil), cfg...)
	}
	return out, nil
}

func canonicalEndpointName(name string) string {
	return strings.ToLower(strings.TrimSpace(name))
}

func sortedRawEndpointNames(m map[string]json.RawMessage) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

func validateSignatureSchemeConfig(cfg *SignatureSchemeConfig) error {
	switch cfg.SignatureEncoding {
	case "", "hex", "base64":
	default:
		return fmt.Errorf("SignatureEncoding %q is not one of {\"\", \"hex\", \"base64\"}", cfg.SignatureEncoding)
	}
	if cfg.Scheme != "hmac-sha256-template" && cfg.Scheme != "sha256-secret-prefix-template" {
		return fmt.Errorf("unknown signature scheme %q", cfg.Scheme)
	}
	if cfg.SigningTemplate == "" {
		return fmt.Errorf("SigningTemplate is empty")
	}
	tokens := templateTokenRE.FindAllString(cfg.SigningTemplate, -1)
	for _, tok := range tokens {
		if !allowedTemplateTokens[tok] {
			return fmt.Errorf("unknown signing template token %q", tok)
		}
	}
	cfg.TimestampHeader = strings.TrimSpace(cfg.TimestampHeader)
	switch cfg.Scheme {
	case "hmac-sha256-template":
		if cfg.TimestampHeader == "" {
			return fmt.Errorf("TimestampHeader is empty")
		}
		cfg.TimestampHeader = http.CanonicalHeaderKey(cfg.TimestampHeader)
		if cfg.SkewWindow <= 0 {
			return fmt.Errorf("SkewWindow must be > 0")
		}
	case "sha256-secret-prefix-template":
		if strings.Contains(cfg.SigningTemplate, "{Timestamp}") {
			return fmt.Errorf("scheme %q does not support {Timestamp}", cfg.Scheme)
		}
		if cfg.TimestampHeader != "" || cfg.SkewWindow != 0 {
			return fmt.Errorf("scheme %q is timestamp-free; TimestampHeader and SkewWindow must be empty", cfg.Scheme)
		}
	}
	cfg.SignatureHeader = strings.TrimSpace(cfg.SignatureHeader)
	if cfg.SignatureHeader == "" {
		return fmt.Errorf("SignatureHeader is empty")
	}
	cfg.SignatureHeader = http.CanonicalHeaderKey(cfg.SignatureHeader)
	if cfg.TimestampHeader != "" && cfg.TimestampHeader == cfg.SignatureHeader {
		return fmt.Errorf("TimestampHeader and SignatureHeader must differ (got %q)", cfg.TimestampHeader)
	}
	if !cfg.VerifiedIngressReceipt {
		cfg.DeliveryIdentityBodyPath = ""
		cfg.DeliveryIdentityCollectionPath = ""
		cfg.DeliveryIdentityCollectionKeyPaths = nil
		cfg.ReceiptHeaderAllowlist = nil
	} else {
		cfg.DeliveryIdentityBodyPath = strings.TrimSpace(cfg.DeliveryIdentityBodyPath)
		cfg.DeliveryIdentityCollectionPath = strings.TrimSpace(cfg.DeliveryIdentityCollectionPath)
		for i, path := range cfg.DeliveryIdentityCollectionKeyPaths {
			cfg.DeliveryIdentityCollectionKeyPaths[i] = strings.TrimSpace(path)
		}
		hasScalarIdentity := cfg.DeliveryIdentityBodyPath != ""
		hasCollectionIdentity := cfg.DeliveryIdentityCollectionPath != "" || len(cfg.DeliveryIdentityCollectionKeyPaths) > 0
		if hasScalarIdentity == hasCollectionIdentity {
			return fmt.Errorf("exactly one delivery identity source is required when VerifiedIngressReceipt is true")
		}
		if !strings.Contains(cfg.SigningTemplate, "{RawBody}") {
			return fmt.Errorf("delivery identity source is configured but SigningTemplate does not include {RawBody}")
		}
		if hasScalarIdentity {
			if _, err := parsePath(cfg.DeliveryIdentityBodyPath); err != nil {
				return fmt.Errorf("DeliveryIdentityBodyPath %q: %v", cfg.DeliveryIdentityBodyPath, err)
			}
		} else {
			if cfg.DeliveryIdentityCollectionPath == "" {
				return fmt.Errorf("DeliveryIdentityCollectionPath is required when DeliveryIdentityCollectionKeyPaths is set")
			}
			if len(cfg.DeliveryIdentityCollectionKeyPaths) == 0 {
				return fmt.Errorf("DeliveryIdentityCollectionKeyPaths is required when DeliveryIdentityCollectionPath is set")
			}
			if _, err := parsePath(cfg.DeliveryIdentityCollectionPath); err != nil {
				return fmt.Errorf("DeliveryIdentityCollectionPath %q: %v", cfg.DeliveryIdentityCollectionPath, err)
			}
			seen := make(map[string]struct{}, len(cfg.DeliveryIdentityCollectionKeyPaths))
			for i, path := range cfg.DeliveryIdentityCollectionKeyPaths {
				if path == "" {
					return fmt.Errorf("DeliveryIdentityCollectionKeyPaths[%d] is empty", i)
				}
				if _, err := parsePath(path); err != nil {
					return fmt.Errorf("DeliveryIdentityCollectionKeyPaths[%d] %q: %v", i, path, err)
				}
				if _, ok := seen[path]; ok {
					return fmt.Errorf("DeliveryIdentityCollectionKeyPaths contains duplicate path %q", path)
				}
				seen[path] = struct{}{}
			}
		}
	}
	cfg.SecretName = strings.TrimSpace(cfg.SecretName)
	if cfg.MethodHeader != "" {
		cfg.MethodHeader = strings.TrimSpace(cfg.MethodHeader)
		if cfg.MethodHeader != "" {
			cfg.MethodHeader = http.CanonicalHeaderKey(cfg.MethodHeader)
		}
	}
	if cfg.URIHeader != "" {
		cfg.URIHeader = strings.TrimSpace(cfg.URIHeader)
		if cfg.URIHeader != "" {
			cfg.URIHeader = http.CanonicalHeaderKey(cfg.URIHeader)
		}
	}
	cfg.ReceiptHeaderAllowlist = canonicalHeaderAllowlist(cfg.ReceiptHeaderAllowlist)
	return nil
}

func canonicalHeaderAllowlist(names []string) []string {
	if len(names) == 0 {
		return nil
	}
	seen := make(map[string]struct{}, len(names))
	out := make([]string, 0, len(names))
	for _, name := range names {
		canonical := http.CanonicalHeaderKey(strings.TrimSpace(name))
		if canonical == "" {
			continue
		}
		key := strings.ToLower(canonical)
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, canonical)
	}
	return out
}

func validateEnvelopeMappingConfig(cfg *EnvelopeMappingConfig, ruleComposites []*IdempotencyKeyComposite) error {
	mode, err := canonicalizeInboundProcessingMode(cfg.InboundProcessingMode)
	if err != nil {
		return err
	}
	cfg.InboundProcessingMode = mode
	hasArrayPath := strings.TrimSpace(cfg.EventArrayPath) != ""
	hasObjectPath := strings.TrimSpace(cfg.EventObjectPath) != ""
	if hasArrayPath == hasObjectPath {
		return fmt.Errorf("exactly one of EventArrayPath or EventObjectPath must be set")
	}
	if hasArrayPath {
		if _, err := parsePath(cfg.EventArrayPath); err != nil {
			return fmt.Errorf("EventArrayPath %q: %v", cfg.EventArrayPath, err)
		}
	}
	if hasObjectPath {
		if _, err := parsePath(cfg.EventObjectPath); err != nil {
			return fmt.Errorf("EventObjectPath %q: %v", cfg.EventObjectPath, err)
		}
	}
	if len(cfg.Rules) == 0 {
		return fmt.Errorf("Rules is empty (a parser with zero rules silently drops every delivery; declare at least one rule)")
	}
	if len(ruleComposites) != 0 && len(ruleComposites) != len(cfg.Rules) {
		return fmt.Errorf("rule idempotency composite sidecar length %d must be zero or equal Rules length %d", len(ruleComposites), len(cfg.Rules))
	}
	for i, r := range cfg.Rules {
		if strings.TrimSpace(r.ObjectKind) == "" {
			return fmt.Errorf("rule %d: ObjectKind is empty or whitespace-only", i)
		}
		if r.SourceIDPath == "" {
			return fmt.Errorf("rule %d: SourceIDPath is empty", i)
		}
		if _, err := parsePath(r.SourceIDPath); err != nil {
			return fmt.Errorf("rule %d SourceIDPath %q: %v", i, r.SourceIDPath, err)
		}
		hasSourceLaneObjectKind := strings.TrimSpace(r.SourceLaneObjectKind) != ""
		hasSourceLaneIDPath := strings.TrimSpace(r.SourceLaneIDPath) != ""
		if hasSourceLaneObjectKind != hasSourceLaneIDPath {
			return fmt.Errorf("rule %d: SourceLaneObjectKind and SourceLaneIDPath must be configured together", i)
		}
		if hasSourceLaneIDPath {
			if _, err := parsePath(r.SourceLaneIDPath); err != nil {
				return fmt.Errorf("rule %d SourceLaneIDPath %q: %v", i, r.SourceLaneIDPath, err)
			}
		}
		var composite *IdempotencyKeyComposite
		if len(ruleComposites) != 0 {
			composite = ruleComposites[i]
		}
		hasLegacyIdempotencyPath := strings.TrimSpace(r.IdempotencyKeyPath) != ""
		if hasLegacyIdempotencyPath == (composite != nil) {
			return fmt.Errorf("rule %d: configure exactly one of IdempotencyKeyPath or IdempotencyKeyComposite", i)
		}
		if composite != nil {
			canonical, err := canonicalizeIdempotencyKeyComposite(*composite)
			if err != nil {
				return fmt.Errorf("rule %d IdempotencyKeyComposite: %v", i, err)
			}
			if compositeDegeneratesToPath(canonical, r.SourceIDPath) {
				return fmt.Errorf("rule %d: one-part IdempotencyKeyComposite must differ from SourceIDPath", i)
			}
			if r.EventTypeFromPath != "" && compositeDegeneratesToPath(canonical, r.EventTypeFromPath) {
				return fmt.Errorf("rule %d: one-part IdempotencyKeyComposite must differ from EventTypeFromPath", i)
			}
			ruleComposites[i] = &canonical
		} else {
			if _, err := parsePath(r.IdempotencyKeyPath); err != nil {
				return fmt.Errorf("rule %d IdempotencyKeyPath %q: %v", i, r.IdempotencyKeyPath, err)
			}
			if strings.TrimSpace(r.IdempotencyKeyPath) == strings.TrimSpace(r.SourceIDPath) {
				return fmt.Errorf("rule %d: IdempotencyKeyPath %q must differ from SourceIDPath", i, r.IdempotencyKeyPath)
			}
			if r.EventTypeFromPath != "" && strings.TrimSpace(r.IdempotencyKeyPath) == strings.TrimSpace(r.EventTypeFromPath) {
				return fmt.Errorf("rule %d: IdempotencyKeyPath %q must differ from EventTypeFromPath", i, r.IdempotencyKeyPath)
			}
		}
		seenPayloadNames := make(map[string]struct{})
		for j, pf := range r.PayloadProjection {
			name := strings.TrimSpace(pf.Name)
			if name == "" {
				return fmt.Errorf("rule %d field %d: PayloadField.Name is empty", i, j)
			}
			if _, dup := seenPayloadNames[pf.Name]; dup {
				return fmt.Errorf("rule %d field %d: PayloadField.Name %q is duplicated within the same rule", i, j, pf.Name)
			}
			seenPayloadNames[pf.Name] = struct{}{}
			mode, err := canonicalizePayloadFieldMode(pf.Mode)
			if err != nil {
				return fmt.Errorf("rule %d field %d: %v", i, j, err)
			}
			cfg.Rules[i].PayloadProjection[j].Mode = mode
		}
		if r.EventTypeFromPath != "" && r.EventTypeLiteral != "" {
			return fmt.Errorf("rule %d: EventTypeFromPath and EventTypeLiteral are mutually exclusive", i)
		}
		if r.EventTypeFromPath == "" && r.EventTypeLiteral == "" {
			return fmt.Errorf("rule %d: one of EventTypeFromPath or EventTypeLiteral must be set", i)
		}
		if r.EventTypeLiteral != "" && strings.TrimSpace(r.EventTypeLiteral) == "" {
			return fmt.Errorf("rule %d: EventTypeLiteral is whitespace-only", i)
		}
		cfg.Rules[i].ObjectKind = strings.TrimSpace(r.ObjectKind)
		cfg.Rules[i].SourceLaneObjectKind = strings.TrimSpace(r.SourceLaneObjectKind)
		cfg.Rules[i].EventTypeLiteral = strings.TrimSpace(r.EventTypeLiteral)
		if r.MatchPath != "" {
			if _, err := parsePath(r.MatchPath); err != nil {
				return fmt.Errorf("rule %d MatchPath %q: %v", i, r.MatchPath, err)
			}
		}
		if r.MatchAll != nil && len(r.MatchAll) == 0 {
			return fmt.Errorf("rule %d: matchAll must contain at least one condition when present", i)
		}
		if len(r.MatchAll) > 0 {
			if strings.TrimSpace(r.MatchPath) != "" || r.MatchEquals != "" {
				return fmt.Errorf("rule %d: matchAll is mutually exclusive with matchPath/matchEquals", i)
			}
			seenPaths := make(map[string]struct{}, len(r.MatchAll))
			for j, cond := range r.MatchAll {
				if cond.Path == "" {
					return fmt.Errorf("rule %d matchAll[%d]: path is empty", i, j)
				}
				path, err := canonicalPathKey(cond.Path)
				if err != nil {
					return fmt.Errorf("rule %d matchAll[%d] path %q: %v", i, j, cond.Path, err)
				}
				if _, dup := seenPaths[path]; dup {
					return fmt.Errorf("rule %d matchAll[%d] repeats path %q; each matchAll path may appear at most once", i, j, strings.TrimSpace(cond.Path))
				}
				seenPaths[path] = struct{}{}
			}
		}
		if r.EventTypeFromPath != "" {
			if _, err := parsePath(r.EventTypeFromPath); err != nil {
				return fmt.Errorf("rule %d EventTypeFromPath %q: %v", i, r.EventTypeFromPath, err)
			}
		}
		for j, pf := range r.PayloadProjection {
			if _, err := parsePath(pf.Path); err != nil {
				return fmt.Errorf("rule %d field %d path %q: %v", i, j, pf.Path, err)
			}
		}
	}
	catchAllIndex := -1
	for i, r := range cfg.Rules {
		if isCatchAllRule(r) {
			if catchAllIndex >= 0 {
				return fmt.Errorf("rule %d: at most one catch-all rule (MatchPath empty) allowed; rule %d is also a catch-all", i, catchAllIndex)
			}
			catchAllIndex = i
		}
	}
	if catchAllIndex >= 0 && catchAllIndex != len(cfg.Rules)-1 {
		return fmt.Errorf("catch-all rule (MatchPath empty) at index %d must be the LAST rule", catchAllIndex)
	}
	type seenRuleSelector struct {
		index      int
		predicates []ruleSelector
	}
	seenSelectors := make(map[string]int)
	seenPredicateSelectors := make([]seenRuleSelector, 0, len(cfg.Rules))
	for i, r := range cfg.Rules {
		sel := selectorKey(r)
		if sel == "" {
			continue
		}
		if first, dup := seenSelectors[sel]; dup {
			return fmt.Errorf("rule %d has the same selector as rule %d", i, first)
		}
		predicates := selectorPredicates(r)
		for _, previous := range seenPredicateSelectors {
			if selectorCovers(previous.predicates, predicates) {
				return fmt.Errorf("rule %d is shadowed by broader selector in rule %d", i, previous.index)
			}
		}
		seenSelectors[sel] = i
		seenPredicateSelectors = append(seenPredicateSelectors, seenRuleSelector{index: i, predicates: predicates})
	}
	return nil
}

func canonicalizeIdempotencyKeyComposite(raw IdempotencyKeyComposite) (IdempotencyKeyComposite, error) {
	version := strings.ToLower(strings.TrimSpace(raw.Version))
	if version != "v1" {
		return IdempotencyKeyComposite{}, fmt.Errorf("Version %q is not supported", raw.Version)
	}
	encoding := strings.ToLower(strings.TrimSpace(raw.Encoding))
	if encoding != "sha256" {
		return IdempotencyKeyComposite{}, fmt.Errorf("Encoding %q is not supported", raw.Encoding)
	}
	if len(raw.Parts) == 0 {
		return IdempotencyKeyComposite{}, fmt.Errorf("Parts is empty")
	}
	canonical := IdempotencyKeyComposite{
		Version:  version,
		Encoding: encoding,
		Parts:    make([]IdempotencyKeyPart, 0, len(raw.Parts)),
	}
	seenPaths := make(map[string]struct{}, len(raw.Parts))
	for i, part := range raw.Parts {
		path := strings.TrimSpace(part.Path)
		if path == "" {
			return IdempotencyKeyComposite{}, fmt.Errorf("Parts[%d].Path is empty", i)
		}
		canonicalPath, err := canonicalIdempotencyPath(path)
		if err != nil {
			return IdempotencyKeyComposite{}, fmt.Errorf("Parts[%d].Path %q: %v", i, path, err)
		}
		if _, duplicate := seenPaths[canonicalPath]; duplicate {
			return IdempotencyKeyComposite{}, fmt.Errorf("Parts[%d].Path %q duplicates an earlier canonical path", i, path)
		}
		seenPaths[canonicalPath] = struct{}{}
		canonical.Parts = append(canonical.Parts, IdempotencyKeyPart{Path: path})
	}
	return canonical, nil
}

func compositeDegeneratesToPath(composite IdempotencyKeyComposite, path string) bool {
	if len(composite.Parts) != 1 {
		return false
	}
	partPath, partErr := canonicalIdempotencyPath(composite.Parts[0].Path)
	targetPath, targetErr := canonicalIdempotencyPath(path)
	return partErr == nil && targetErr == nil && partPath == targetPath
}

func canonicalIdempotencyPath(path string) (string, error) {
	segments, err := parsePath(path)
	if err != nil {
		return "", err
	}
	var framed bytes.Buffer
	writeConfigIdentityCount(&framed, len(segments))
	for _, segment := range segments {
		if segment.isIndex {
			writeConfigIdentityFrame(&framed, []byte("index"))
			writeConfigIdentityFrame(&framed, []byte(strconv.Itoa(segment.index)))
			continue
		}
		writeConfigIdentityFrame(&framed, []byte("key"))
		writeConfigIdentityFrame(&framed, []byte(segment.key))
	}
	return framed.String(), nil
}

func writeConfigIdentityFrame(buf *bytes.Buffer, value []byte) {
	var size [4]byte
	binary.BigEndian.PutUint32(size[:], uint32(len(value)))
	buf.Write(size[:])
	buf.Write(value)
}

func writeConfigIdentityCount(buf *bytes.Buffer, count int) {
	var encoded [4]byte
	binary.BigEndian.PutUint32(encoded[:], uint32(count))
	buf.Write(encoded[:])
}

func isCatchAllRule(r EnvelopeMappingRule) bool {
	return r.MatchAll == nil && strings.TrimSpace(r.MatchPath) == ""
}

func selectorKey(r EnvelopeMappingRule) string {
	if r.MatchAll == nil {
		path, _ := canonicalPathKey(r.MatchPath)
		if path == "" {
			return ""
		}
		return "single\x00" + path + "\x00" + r.MatchEquals
	}
	parts := make([]string, 0, len(r.MatchAll))
	for _, cond := range r.MatchAll {
		path, _ := canonicalPathKey(cond.Path)
		parts = append(parts, path+"\x00"+cond.Equals)
	}
	sort.Strings(parts)
	if len(parts) == 1 {
		return "single\x00" + parts[0]
	}
	return "all\x00" + strings.Join(parts, "\x1e")
}

func selectorPredicates(r EnvelopeMappingRule) []ruleSelector {
	if r.MatchAll == nil {
		path, _ := canonicalPathKey(r.MatchPath)
		if path == "" {
			return nil
		}
		return []ruleSelector{{path: path, equals: r.MatchEquals}}
	}
	predicates := make([]ruleSelector, 0, len(r.MatchAll))
	for _, cond := range r.MatchAll {
		path, _ := canonicalPathKey(cond.Path)
		predicates = append(predicates, ruleSelector{path: path, equals: cond.Equals})
	}
	return predicates
}

func selectorCovers(previous, current []ruleSelector) bool {
	if len(previous) == 0 || len(previous) > len(current) {
		return false
	}
	currentByPath := make(map[string]string, len(current))
	for _, predicate := range current {
		currentByPath[predicate.path] = predicate.equals
	}
	for _, predicate := range previous {
		currentEquals, exists := currentByPath[predicate.path]
		if !exists {
			return false
		}
		if predicate.equals != "" && predicate.equals != currentEquals {
			return false
		}
	}
	return true
}

func canonicalPathKey(path string) (string, error) {
	segs, err := parsePath(path)
	if err != nil {
		return "", err
	}
	if len(segs) == 0 {
		return ".", nil
	}
	parts := make([]string, 0, len(segs))
	for _, seg := range segs {
		if seg.isIndex {
			parts = append(parts, "i:"+strconv.Itoa(seg.index))
			continue
		}
		parts = append(parts, "k:"+seg.key)
	}
	return strings.Join(parts, "\x1f"), nil
}

func validateEndpointEnvelopeMappingContracts(configs map[string]EnvelopeMappingConfig) error {
	type endpointMode struct {
		endpoint string
		mode     InboundProcessingMode
	}
	seen := make(map[string]endpointMode)
	for endpoint, cfg := range configs {
		mode := cfg.InboundProcessingMode
		for _, rule := range cfg.Rules {
			objectKind := strings.TrimSpace(rule.ObjectKind)
			if objectKind == "" {
				continue
			}
			if existing, ok := seen[objectKind]; ok && existing.mode != mode {
				return fmt.Errorf("endpointConfigs[%q] and endpointConfigs[%q] use conflicting inboundProcessingMode values for objectKind %q; connector inbound queue envelopes do not carry endpoint identity", existing.endpoint, endpoint, objectKind)
			}
			seen[objectKind] = endpointMode{endpoint: endpoint, mode: mode}
		}
	}
	return nil
}

func canonicalizeInboundProcessingMode(raw InboundProcessingMode) (InboundProcessingMode, error) {
	mode := InboundProcessingMode(strings.ToLower(strings.TrimSpace(string(raw))))
	if mode == "" {
		return "", nil
	}
	switch mode {
	case InboundProcessingModeEnrichmentRequired, InboundProcessingModePayloadOnlyAllowed:
		return mode, nil
	default:
		return "", fmt.Errorf("InboundProcessingMode %q is not one of {\"%s\", \"%s\"}", raw, InboundProcessingModeEnrichmentRequired, InboundProcessingModePayloadOnlyAllowed)
	}
}

func envelopeMappingConfigToWire(cfg EnvelopeMappingConfig, ruleComposites []*IdempotencyKeyComposite) envelopeMappingConfigWire {
	wire := envelopeMappingConfigWire{
		EventArrayPath:        cfg.EventArrayPath,
		EventObjectPath:       cfg.EventObjectPath,
		InboundProcessingMode: string(cfg.InboundProcessingMode),
		Rules:                 make([]envelopeMappingRuleWire, 0, len(cfg.Rules)),
	}
	for ruleIndex, rule := range cfg.Rules {
		out := envelopeMappingRuleWire{
			MatchPath:            rule.MatchPath,
			MatchEquals:          rule.MatchEquals,
			EventTypeFromPath:    rule.EventTypeFromPath,
			EventTypeLiteral:     rule.EventTypeLiteral,
			NamespaceFromCtx:     rule.NamespaceFromCtx,
			ObjectKind:           rule.ObjectKind,
			SourceIDPath:         rule.SourceIDPath,
			SourceLaneObjectKind: rule.SourceLaneObjectKind,
			SourceLaneIDPath:     rule.SourceLaneIDPath,
			IdempotencyKeyPath:   rule.IdempotencyKeyPath,
			PayloadProjection:    make([]payloadFieldWire, 0, len(rule.PayloadProjection)),
		}
		if len(ruleComposites) != 0 {
			if composite := ruleComposites[ruleIndex]; composite != nil {
				parts := make([]idempotencyKeyPartWire, 0, len(composite.Parts))
				for _, part := range composite.Parts {
					parts = append(parts, idempotencyKeyPartWire{Path: part.Path})
				}
				out.IdempotencyKeyComposite = &idempotencyKeyCompositeWire{
					Version:  composite.Version,
					Encoding: composite.Encoding,
					Parts:    parts,
				}
			}
		}
		if rule.MatchAll != nil {
			matchAll := append([]MatchCondition(nil), rule.MatchAll...)
			sort.SliceStable(matchAll, func(i, j int) bool {
				leftPath, _ := canonicalPathKey(matchAll[i].Path)
				rightPath, _ := canonicalPathKey(matchAll[j].Path)
				if leftPath != rightPath {
					return leftPath < rightPath
				}
				if matchAll[i].Equals != matchAll[j].Equals {
					return matchAll[i].Equals < matchAll[j].Equals
				}
				return strings.TrimSpace(matchAll[i].Path) < strings.TrimSpace(matchAll[j].Path)
			})
			matchAllWire := make([]matchConditionWire, 0, len(matchAll))
			for _, cond := range matchAll {
				matchAllWire = append(matchAllWire, matchConditionWire{
					Path:   cond.Path,
					Equals: cond.Equals,
				})
			}
			out.MatchAll = &matchAllWire
		}
		for _, field := range rule.PayloadProjection {
			out.PayloadProjection = append(out.PayloadProjection, payloadFieldWire{
				Name: field.Name,
				Path: field.Path,
				Mode: field.Mode,
			})
		}
		wire.Rules = append(wire.Rules, out)
	}
	return wire
}

func decodeStrict(raw []byte, out any) error {
	dec := json.NewDecoder(bytes.NewReader(raw))
	dec.DisallowUnknownFields()
	if err := dec.Decode(out); err != nil {
		return err
	}
	return ensureJSONEOF(dec)
}

func ensureJSONEOF(dec *json.Decoder) error {
	var extra any
	if err := dec.Decode(&extra); err != nil {
		if err == io.EOF {
			return nil
		}
		return err
	}
	return fmt.Errorf("unexpected trailing JSON value")
}

type pathSegment struct {
	key     string
	index   int
	isIndex bool
}

func parsePath(path string) ([]pathSegment, error) {
	if path == "" {
		return nil, fmt.Errorf("path is empty")
	}
	if path == "." {
		return nil, nil
	}
	if !strings.HasPrefix(path, ".") {
		return nil, fmt.Errorf("path must start with '.'")
	}
	rest := path[1:]
	var segs []pathSegment
	for rest != "" {
		if strings.HasPrefix(rest, "[") {
			end := strings.Index(rest, "]")
			if end < 0 {
				return nil, fmt.Errorf("unclosed '[' in path %q", path)
			}
			idxStr := rest[1:end]
			idx, err := strconv.Atoi(idxStr)
			if err != nil {
				return nil, fmt.Errorf("non-integer index %q in path %q", idxStr, path)
			}
			segs = append(segs, pathSegment{index: idx, isIndex: true})
			rest = rest[end+1:]
			continue
		}
		end := strings.IndexAny(rest, ".[")
		var field string
		if end < 0 {
			field = rest
			rest = ""
		} else {
			field = rest[:end]
			rest = rest[end:]
			if strings.HasPrefix(rest, ".") {
				rest = rest[1:]
			}
		}
		if field == "" {
			return nil, fmt.Errorf("empty field name in path %q", path)
		}
		segs = append(segs, pathSegment{key: field})
	}
	return segs, nil
}
