package tenantlayoutbundleauthorityv1

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"
	"testing"

	"github.com/pingcap-inc/nexuscrm-contract/pkg/catalogpublicationv1"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/internal/testvectors"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/layoutbundlekeyv1"
)

func TestImmutableConformanceVectors(t *testing.T) {
	document := testvectors.DecodeVectorDocument[vectorDocument](t, ConformanceVectorsJSON())
	if document.SchemaVersion != "nexuscrm.layout-bundle-tenant-authority.conformance/v1" {
		t.Fatalf("schema_version = %q", document.SchemaVersion)
	}
	if document.NamespaceValueVectors == nil ||
		document.BindingValueVectors == nil ||
		document.SlotValueVectors == nil ||
		document.CommandIDVectors == nil ||
		document.GrammarVectors == nil ||
		document.NamespaceRequestVectors == nil ||
		document.BindingRequestVectors == nil ||
		document.SlotRequestVectors == nil ||
		document.EvidenceVectors == nil ||
		document.ULEB128Vectors == nil ||
		document.RejectionVectors == nil {
		t.Fatal("every vector collection must be a present JSON array")
	}

	seenNames := map[string]bool{}
	namespaces := map[string]TenantNamespaceReservationV1{}
	for _, vector := range document.NamespaceValueVectors {
		t.Run("namespace-value/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			value := vector.Value.value()
			canonical, err := CanonicalNamespaceValueBytes(value)
			if err != nil {
				t.Fatal(err)
			}
			assertCanonicalHex(t, canonical, vector.CanonicalBytesHex)
			digestValue, err := NamespaceValueDigest(value)
			if err != nil || digestValue != vector.ValueDigest {
				t.Fatalf("namespace digest = %q, %v", digestValue, err)
			}
			namespaces[vector.Name] = value
		})
	}
	requireNames(t, namespaces, "namespace-active", "namespace-tombstone")

	bindings := map[string]TenantOrgNamespaceBindingV1{}
	for _, vector := range document.BindingValueVectors {
		t.Run("binding-value/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			value := vector.Value.value()
			canonical, err := CanonicalOrgBindingValueBytes(value)
			if err != nil {
				t.Fatal(err)
			}
			assertCanonicalHex(t, canonical, vector.CanonicalBytesHex)
			digestValue, err := OrgBindingValueDigest(value)
			if err != nil || digestValue != vector.ValueDigest {
				t.Fatalf("binding digest = %q, %v", digestValue, err)
			}
			bindings[vector.Name] = value
		})
	}
	requireNames(t, bindings, "binding-active", "binding-tombstone")

	slots := map[string]TenantLayoutBundleSlotV1{}
	for _, vector := range document.SlotValueVectors {
		t.Run("slot-value/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			value := vector.Value.value()
			canonical, err := CanonicalSlotValueBytes(value)
			if err != nil {
				t.Fatal(err)
			}
			assertCanonicalHex(t, canonical, vector.CanonicalBytesHex)
			digestValue, err := SlotValueDigest(value)
			if err != nil || digestValue != vector.ValueDigest {
				t.Fatalf("slot digest = %q, %v", digestValue, err)
			}
			slots[vector.Name] = value
		})
	}
	requireNames(t, slots, "slot-active", "slot-tombstone")

	for _, vector := range document.CommandIDVectors {
		t.Run("command-id/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			err := ValidateCommandID(CommandID(vector.CommandID))
			if vector.Valid && err != nil {
				t.Fatalf("valid CommandID rejected: %v", err)
			}
			if !vector.Valid && err == nil {
				t.Fatal("invalid CommandID accepted")
			}
		})
	}

	for _, vector := range document.GrammarVectors {
		t.Run("grammar/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			var err error
			value := vector.Value
			if vector.Mutation == "invalid_utf8" {
				value = string([]byte{0xff})
			}
			switch vector.Kind {
			case "tenant_namespace":
				err = ValidateTenantNamespaceID(value)
			case "catalog_org_id":
				err = ValidateCatalogOrgID(value)
			default:
				t.Fatalf("unknown grammar kind %q", vector.Kind)
			}
			if vector.Valid && err != nil {
				t.Fatalf("valid grammar vector rejected: %v", err)
			}
			if !vector.Valid && err == nil {
				t.Fatal("invalid grammar vector accepted")
			}
		})
	}

	namespaceRequests := map[string]TenantNamespaceCommandRequest{}
	requestDigests := map[string]string{}
	for _, vector := range document.NamespaceRequestVectors {
		t.Run("namespace-request/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			if err := ValidateCommandID(CommandID(vector.CommandID)); err != nil {
				t.Fatal(err)
			}
			request := vector.Request.value()
			canonical, err := CanonicalNamespaceCommandRequestBytes(request)
			if err != nil {
				t.Fatal(err)
			}
			assertCanonicalHex(t, canonical, vector.CanonicalBytesHex)
			digestValue, err := NamespaceCommandRequestDigest(request)
			if err != nil || digestValue != vector.RequestDigest {
				t.Fatalf("namespace request digest = %q, %v", digestValue, err)
			}
			namespaceRequests[vector.Name] = request
			requestDigests["namespace/"+vector.Name] = digestValue
		})
	}
	requireNames(t, namespaceRequests, "namespace-reserve-absent", "namespace-tombstone-exact")

	bindingRequests := map[string]TenantOrgNamespaceBindingCommandRequest{}
	for _, vector := range document.BindingRequestVectors {
		t.Run("binding-request/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			if err := ValidateCommandID(CommandID(vector.CommandID)); err != nil {
				t.Fatal(err)
			}
			request := vector.Request.value()
			canonical, err := CanonicalOrgBindingCommandRequestBytes(request)
			if err != nil {
				t.Fatal(err)
			}
			assertCanonicalHex(t, canonical, vector.CanonicalBytesHex)
			digestValue, err := OrgBindingCommandRequestDigest(request)
			if err != nil || digestValue != vector.RequestDigest {
				t.Fatalf("binding request digest = %q, %v", digestValue, err)
			}
			bindingRequests[vector.Name] = request
			requestDigests["binding/"+vector.Name] = digestValue
		})
	}
	requireNames(t, bindingRequests, "binding-bind-absent", "binding-tombstone-exact")

	slotRequests := map[string]TenantLayoutBundleSlotCommandRequest{}
	for _, vector := range document.SlotRequestVectors {
		t.Run("slot-request/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			if err := ValidateCommandID(CommandID(vector.CommandID)); err != nil {
				t.Fatal(err)
			}
			request := vector.Request.value()
			canonical, err := CanonicalSlotCommandRequestBytes(request)
			if err != nil {
				t.Fatal(err)
			}
			assertCanonicalHex(t, canonical, vector.CanonicalBytesHex)
			digestValue, err := SlotCommandRequestDigest(request)
			if err != nil || digestValue != vector.RequestDigest {
				t.Fatalf("slot request digest = %q, %v", digestValue, err)
			}
			slotRequests[vector.Name] = request
			requestDigests["slot/"+vector.Name] = digestValue
		})
	}
	requireNames(t, slotRequests, "slot-create-absent", "slot-retire-exact")
	if len(requestDigests) != 6 {
		t.Fatalf("request digest count = %d", len(requestDigests))
	}
	digestOwners := map[string]string{}
	for name, digestValue := range requestDigests {
		if owner, duplicate := digestOwners[digestValue]; duplicate {
			t.Fatalf("request kinds %s and %s share digest %s", owner, name, digestValue)
		}
		digestOwners[digestValue] = name
	}

	evidenceValues := map[string]TenantLayoutBundleAuthorityEvidenceV1{}
	for _, vector := range document.EvidenceVectors {
		t.Run("evidence/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			value := vector.Evidence.value(t)
			canonical, err := CanonicalEvidenceBytes(value)
			if err != nil {
				t.Fatal(err)
			}
			assertCanonicalHex(t, canonical, vector.CanonicalBytesHex)
			digestValue, err := EvidenceDigest(value)
			if err != nil || digestValue != vector.EvidenceDigest {
				t.Fatalf("evidence digest = %q, %v", digestValue, err)
			}
			evidenceValues[vector.Name] = value
		})
	}
	requireNames(t, evidenceValues, "baseline", "utf8-source", "multibyte-uleb128-source")

	for _, vector := range document.ULEB128Vectors {
		name := "uleb128-" + vector.ValueDecimal
		t.Run(name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, name)
			value := parseCanonicalUint(t, "value_decimal", vector.ValueDecimal)
			var encoded [binary.MaxVarintLen64]byte
			count := binary.PutUvarint(encoded[:], value)
			if got := hex.EncodeToString(encoded[:count]); got != vector.EncodedHex {
				t.Fatalf("encoded bytes = %s, want %s", got, vector.EncodedHex)
			}
		})
	}

	rejections := map[string]rejectionVector{}
	for _, vector := range document.RejectionVectors {
		t.Run("rejection/"+vector.Name, func(t *testing.T) {
			testvectors.AssertUniqueVectorName(t, seenNames, vector.Name)
			err := applyRejectionVector(
				t,
				vector,
				slots,
				namespaceRequests,
				bindingRequests,
				slotRequests,
				evidenceValues,
			)
			if err == nil || !strings.Contains(err.Error(), vector.ErrorContains) {
				t.Fatalf("rejection error = %v, want substring %q", err, vector.ErrorContains)
			}
			rejections[vector.Name] = vector
		})
	}
	requireNames(
		t,
		rejections,
		"namespace-unknown-operation-rejected",
		"namespace-reserve-tombstone-state-rejected",
		"namespace-reserve-exact-prior-rejected",
		"namespace-absent-prior-digest-rejected",
		"binding-unknown-operation-rejected",
		"binding-bind-tombstone-state-rejected",
		"binding-bind-exact-prior-rejected",
		"binding-absent-prior-digest-rejected",
		"slot-unknown-operation-rejected",
		"slot-create-tombstone-state-rejected",
		"slot-create-exact-prior-rejected",
		"slot-absent-prior-digest-rejected",
	)
}

func TestConformanceVectorsJSONReturnsDefensiveCopy(t *testing.T) {
	testvectors.AssertDefensiveCopy(t, ConformanceVectorsJSON)
}

func TestCanonicalDecimalUint64VectorParsing(t *testing.T) {
	for _, raw := range []string{"0", "1", "18446744073709551615"} {
		if _, err := parseCanonicalUintText(raw); err != nil {
			t.Fatalf("canonical uint64 %q rejected: %v", raw, err)
		}
	}
	for _, raw := range []string{
		"",
		"00",
		"01",
		"-1",
		"+1",
		" 1",
		"1 ",
		"18446744073709551616",
	} {
		if _, err := parseCanonicalUintText(raw); err == nil {
			t.Fatalf("non-canonical uint64 %q accepted", raw)
		}
	}
}

func applyRejectionVector(
	t *testing.T,
	vector rejectionVector,
	slots map[string]TenantLayoutBundleSlotV1,
	namespaceRequests map[string]TenantNamespaceCommandRequest,
	bindingRequests map[string]TenantOrgNamespaceBindingCommandRequest,
	slotRequests map[string]TenantLayoutBundleSlotCommandRequest,
	evidenceValues map[string]TenantLayoutBundleAuthorityEvidenceV1,
) error {
	t.Helper()
	switch vector.Target {
	case "slot_value":
		value, exists := slots[vector.BaseVector]
		if !exists {
			t.Fatalf("unknown slot base %q", vector.BaseVector)
		}
		switch vector.Mutation {
		case "owner_kind=app":
			value.BundleKey.SlotOwnerKind = layoutbundlekeyv1.SlotOwnerKindApp
		case "namespace=reserved_system":
			value.BundleKey.SlotOwnerNamespace = ReservedSystemNamespace
		case "local_key=uppercase":
			value.BundleKey.LocalComponentKey = componentKey("A")
		default:
			t.Fatalf("unknown slot mutation %q", vector.Mutation)
		}
		return ValidateSlotValue(value)
	case "namespace_request":
		value, exists := namespaceRequests[vector.BaseVector]
		if !exists {
			t.Fatalf("unknown namespace request base %q", vector.BaseVector)
		}
		switch vector.Mutation {
		case "target=other":
			value.TargetTenantNamespaceID = "com.example.other"
		case "operation=UNKNOWN":
			value.Operation = NamespaceOperation("UNKNOWN")
		case "desired_state=TOMBSTONE":
			value.DesiredNamespaceValue.State = NamespaceStateTombstone
		case "prior=EXACT_active":
			value.ExpectedPriorNamespace = TenantNamespaceExpectedPrior{
				Variant: NamespaceExpectedPriorExact,
				NamespaceValueDigest: mustNamespaceDigest(
					t,
					value.DesiredNamespaceValue,
				),
			}
		case "absent_prior_digest=nonempty":
			value.ExpectedPriorNamespace.NamespaceValueDigest = digest("1")
		case "prior_digest=wrong":
			value.ExpectedPriorNamespace.NamespaceValueDigest = digest("9")
		default:
			t.Fatalf("unknown namespace request mutation %q", vector.Mutation)
		}
		return ValidateNamespaceCommandRequest(value)
	case "binding_request":
		value, exists := bindingRequests[vector.BaseVector]
		if !exists {
			t.Fatalf("unknown binding request base %q", vector.BaseVector)
		}
		switch vector.Mutation {
		case "target_org=other":
			value.TargetOrgID = "org-2"
		case "operation=UNKNOWN":
			value.Operation = BindingOperation("UNKNOWN")
		case "desired_state=TOMBSTONE":
			value.DesiredBindingValue.State = OrgBindingStateTombstone
		case "prior=EXACT_active":
			value.ExpectedPriorBinding = TenantOrgBindingExpectedPrior{
				Variant:            BindingExpectedPriorExact,
				BindingValueDigest: mustBindingDigest(t, value.DesiredBindingValue),
			}
		case "absent_prior_digest=nonempty":
			value.ExpectedPriorBinding.BindingValueDigest = digest("1")
		case "namespace_digest=wrong":
			value.ExpectedNamespaceReservationDigest = digest("9")
		case "prior_digest=wrong":
			value.ExpectedPriorBinding.BindingValueDigest = digest("9")
		default:
			t.Fatalf("unknown binding request mutation %q", vector.Mutation)
		}
		return ValidateOrgBindingCommandRequest(value)
	case "slot_request":
		value, exists := slotRequests[vector.BaseVector]
		if !exists {
			t.Fatalf("unknown slot request base %q", vector.BaseVector)
		}
		switch vector.Mutation {
		case "target_key=other":
			value.TargetBundleKey.LocalComponentKey = componentKey("2")
		case "operation=UNKNOWN":
			value.Operation = SlotOperation("UNKNOWN")
		case "desired_state=TOMBSTONE":
			value.DesiredSlotValue.State = SlotStateTombstone
		case "prior=EXACT_active":
			value.ExpectedPriorSlot = TenantSlotExpectedPrior{
				Variant:         SlotExpectedPriorExact,
				SlotValueDigest: mustSlotDigest(t, value.DesiredSlotValue),
			}
		case "absent_prior_digest=nonempty":
			value.ExpectedPriorSlot.SlotValueDigest = digest("1")
		case "namespace_digest=wrong":
			value.ExpectedNamespaceReservationDigest = digest("9")
		case "binding_digest=wrong":
			value.ExpectedAuthorizingOrgBindingDigest = digest("9")
		case "prior_digest=wrong":
			value.ExpectedPriorSlot.SlotValueDigest = digest("9")
		default:
			t.Fatalf("unknown slot request mutation %q", vector.Mutation)
		}
		return ValidateSlotCommandRequest(value)
	case "evidence":
		value, exists := evidenceValues[vector.BaseVector]
		if !exists {
			t.Fatalf("unknown evidence base %q", vector.BaseVector)
		}
		switch vector.Mutation {
		case "namespace_state=TOMBSTONE":
			value.NamespaceReservationState = NamespaceStateTombstone
		case "binding_state=TOMBSTONE":
			value.OrgBindingState = OrgBindingStateTombstone
		case "slot_state=TOMBSTONE":
			value.SlotState = SlotStateTombstone
		default:
			t.Fatalf("unknown evidence mutation %q", vector.Mutation)
		}
		return ValidateEvidence(value)
	default:
		t.Fatalf("unknown rejection target %q", vector.Target)
		return nil
	}
}

func assertCanonicalHex(t *testing.T, canonical []byte, want string) {
	t.Helper()
	wantBytes, err := hex.DecodeString(want)
	if err != nil {
		t.Fatalf("golden canonical hex is invalid: %v", err)
	}
	if bytes.Equal(canonical, wantBytes) {
		return
	}
	firstDifference := 0
	for firstDifference < len(canonical) &&
		firstDifference < len(wantBytes) &&
		canonical[firstDifference] == wantBytes[firstDifference] {
		firstDifference++
	}
	t.Fatalf(
		"canonical bytes differ at offset %d (got length %d, want %d)",
		firstDifference,
		len(canonical),
		len(wantBytes),
	)
}

func requireNames[T any](t *testing.T, values map[string]T, required ...string) {
	t.Helper()
	for _, name := range required {
		if _, exists := values[name]; !exists {
			t.Fatalf("required vector %q is absent", name)
		}
	}
}

func parseCanonicalUint(t *testing.T, label, raw string) uint64 {
	t.Helper()
	value, err := parseCanonicalUintText(raw)
	if err != nil {
		t.Fatalf("%s = %q is not a canonical uint64: %v", label, raw, err)
	}
	return value
}

func parseCanonicalUintText(raw string) (uint64, error) {
	value, err := strconv.ParseUint(raw, 10, 64)
	if err != nil {
		return 0, err
	}
	if strconv.FormatUint(value, 10) != raw {
		return 0, fmt.Errorf("canonical form is %q", strconv.FormatUint(value, 10))
	}
	return value, nil
}

type vectorDocument struct {
	SchemaVersion           string                   `json:"schema_version"`
	NamespaceValueVectors   []namespaceValueVector   `json:"namespace_value_vectors"`
	BindingValueVectors     []bindingValueVector     `json:"binding_value_vectors"`
	SlotValueVectors        []slotValueVector        `json:"slot_value_vectors"`
	CommandIDVectors        []commandIDVector        `json:"command_id_vectors"`
	GrammarVectors          []grammarVector          `json:"grammar_vectors"`
	NamespaceRequestVectors []namespaceRequestVector `json:"namespace_request_vectors"`
	BindingRequestVectors   []bindingRequestVector   `json:"binding_request_vectors"`
	SlotRequestVectors      []slotRequestVector      `json:"slot_request_vectors"`
	EvidenceVectors         []evidenceVector         `json:"evidence_vectors"`
	ULEB128Vectors          []uleb128Vector          `json:"uleb128_vectors"`
	RejectionVectors        []rejectionVector        `json:"rejection_vectors"`
}

type namespaceValueVector struct {
	Name              string               `json:"name"`
	Value             vectorNamespaceValue `json:"value"`
	CanonicalBytesHex string               `json:"canonical_bytes_hex"`
	ValueDigest       string               `json:"value_digest"`
}

type bindingValueVector struct {
	Name              string             `json:"name"`
	Value             vectorBindingValue `json:"value"`
	CanonicalBytesHex string             `json:"canonical_bytes_hex"`
	ValueDigest       string             `json:"value_digest"`
}

type slotValueVector struct {
	Name              string          `json:"name"`
	Value             vectorSlotValue `json:"value"`
	CanonicalBytesHex string          `json:"canonical_bytes_hex"`
	ValueDigest       string          `json:"value_digest"`
}

type commandIDVector struct {
	Name      string `json:"name"`
	CommandID string `json:"command_id"`
	Valid     bool   `json:"valid"`
}

type grammarVector struct {
	Name     string `json:"name"`
	Kind     string `json:"kind"`
	Value    string `json:"value"`
	Mutation string `json:"mutation"`
	Valid    bool   `json:"valid"`
}

type namespaceRequestVector struct {
	Name              string                 `json:"name"`
	CommandID         string                 `json:"command_id"`
	Request           vectorNamespaceRequest `json:"request"`
	CanonicalBytesHex string                 `json:"canonical_bytes_hex"`
	RequestDigest     string                 `json:"request_digest"`
}

type bindingRequestVector struct {
	Name              string               `json:"name"`
	CommandID         string               `json:"command_id"`
	Request           vectorBindingRequest `json:"request"`
	CanonicalBytesHex string               `json:"canonical_bytes_hex"`
	RequestDigest     string               `json:"request_digest"`
}

type slotRequestVector struct {
	Name              string            `json:"name"`
	CommandID         string            `json:"command_id"`
	Request           vectorSlotRequest `json:"request"`
	CanonicalBytesHex string            `json:"canonical_bytes_hex"`
	RequestDigest     string            `json:"request_digest"`
}

type evidenceVector struct {
	Name              string         `json:"name"`
	Evidence          vectorEvidence `json:"evidence"`
	CanonicalBytesHex string         `json:"canonical_bytes_hex"`
	EvidenceDigest    string         `json:"evidence_digest"`
}

type uleb128Vector struct {
	ValueDecimal string `json:"value_decimal"`
	EncodedHex   string `json:"encoded_hex"`
}

type rejectionVector struct {
	Name          string `json:"name"`
	Target        string `json:"target"`
	BaseVector    string `json:"base_vector"`
	Mutation      string `json:"mutation"`
	ErrorContains string `json:"error_contains"`
}

type vectorNamespaceValue struct {
	ValueVersion      string `json:"value_version"`
	TenantNamespaceID string `json:"tenant_namespace_id"`
	State             string `json:"state"`
}

func (value vectorNamespaceValue) value() TenantNamespaceReservationV1 {
	return TenantNamespaceReservationV1{
		ValueVersion:      value.ValueVersion,
		TenantNamespaceID: value.TenantNamespaceID,
		State:             NamespaceState(value.State),
	}
}

type vectorBindingValue struct {
	ValueVersion      string `json:"value_version"`
	OrgID             string `json:"org_id"`
	TenantNamespaceID string `json:"tenant_namespace_id"`
	State             string `json:"state"`
}

func (value vectorBindingValue) value() TenantOrgNamespaceBindingV1 {
	return TenantOrgNamespaceBindingV1{
		ValueVersion:      value.ValueVersion,
		OrgID:             value.OrgID,
		TenantNamespaceID: value.TenantNamespaceID,
		State:             OrgBindingState(value.State),
	}
}

type vectorBundleKey struct {
	BundleKeyVersion   string `json:"bundle_key_version"`
	SlotOwnerKind      string `json:"slot_owner_kind"`
	SlotOwnerNamespace string `json:"slot_owner_namespace"`
	LocalComponentKey  string `json:"local_component_key"`
}

func (value vectorBundleKey) value() BundleKey {
	return BundleKey{
		BundleKeyVersion:   value.BundleKeyVersion,
		SlotOwnerKind:      layoutbundlekeyv1.SlotOwnerKind(value.SlotOwnerKind),
		SlotOwnerNamespace: value.SlotOwnerNamespace,
		LocalComponentKey:  layoutbundlekeyv1.LocalComponentKey(value.LocalComponentKey),
	}
}

type vectorSlotValue struct {
	ValueVersion string          `json:"value_version"`
	BundleKey    vectorBundleKey `json:"bundle_key"`
	State        string          `json:"state"`
}

func (value vectorSlotValue) value() TenantLayoutBundleSlotV1 {
	return TenantLayoutBundleSlotV1{
		ValueVersion: value.ValueVersion,
		BundleKey:    value.BundleKey.value(),
		State:        SlotState(value.State),
	}
}

type vectorNamespaceExpectedPrior struct {
	Variant              string `json:"variant"`
	NamespaceValueDigest string `json:"namespace_value_digest"`
}

type vectorBindingExpectedPrior struct {
	Variant            string `json:"variant"`
	BindingValueDigest string `json:"binding_value_digest"`
}

type vectorSlotExpectedPrior struct {
	Variant         string `json:"variant"`
	SlotValueDigest string `json:"slot_value_digest"`
}

type vectorNamespaceRequest struct {
	Operation               string                       `json:"operation"`
	TargetTenantNamespaceID string                       `json:"target_tenant_namespace_id"`
	DesiredNamespaceValue   vectorNamespaceValue         `json:"desired_namespace_value"`
	ExpectedPriorNamespace  vectorNamespaceExpectedPrior `json:"expected_prior_namespace"`
}

func (value vectorNamespaceRequest) value() TenantNamespaceCommandRequest {
	return TenantNamespaceCommandRequest{
		Operation:               NamespaceOperation(value.Operation),
		TargetTenantNamespaceID: value.TargetTenantNamespaceID,
		DesiredNamespaceValue:   value.DesiredNamespaceValue.value(),
		ExpectedPriorNamespace: TenantNamespaceExpectedPrior{
			Variant:              NamespaceExpectedPriorVariant(value.ExpectedPriorNamespace.Variant),
			NamespaceValueDigest: value.ExpectedPriorNamespace.NamespaceValueDigest,
		},
	}
}

type vectorBindingRequest struct {
	Operation                          string                     `json:"operation"`
	TargetOrgID                        string                     `json:"target_org_id"`
	DesiredBindingValue                vectorBindingValue         `json:"desired_binding_value"`
	ExpectedNamespaceReservationDigest string                     `json:"expected_namespace_reservation_digest"`
	ExpectedPriorBinding               vectorBindingExpectedPrior `json:"expected_prior_binding"`
}

func (value vectorBindingRequest) value() TenantOrgNamespaceBindingCommandRequest {
	return TenantOrgNamespaceBindingCommandRequest{
		Operation:                          BindingOperation(value.Operation),
		TargetOrgID:                        value.TargetOrgID,
		DesiredBindingValue:                value.DesiredBindingValue.value(),
		ExpectedNamespaceReservationDigest: value.ExpectedNamespaceReservationDigest,
		ExpectedPriorBinding: TenantOrgBindingExpectedPrior{
			Variant:            BindingExpectedPriorVariant(value.ExpectedPriorBinding.Variant),
			BindingValueDigest: value.ExpectedPriorBinding.BindingValueDigest,
		},
	}
}

type vectorSlotRequest struct {
	Operation                           string                  `json:"operation"`
	AuthorizingOrgID                    string                  `json:"authorizing_org_id"`
	TargetBundleKey                     vectorBundleKey         `json:"target_bundle_key"`
	DesiredSlotValue                    vectorSlotValue         `json:"desired_slot_value"`
	ExpectedNamespaceReservationDigest  string                  `json:"expected_namespace_reservation_digest"`
	ExpectedAuthorizingOrgBindingDigest string                  `json:"expected_authorizing_org_binding_digest"`
	ExpectedPriorSlot                   vectorSlotExpectedPrior `json:"expected_prior_slot"`
}

func (value vectorSlotRequest) value() TenantLayoutBundleSlotCommandRequest {
	return TenantLayoutBundleSlotCommandRequest{
		Operation:                           SlotOperation(value.Operation),
		AuthorizingOrgID:                    value.AuthorizingOrgID,
		TargetBundleKey:                     value.TargetBundleKey.value(),
		DesiredSlotValue:                    value.DesiredSlotValue.value(),
		ExpectedNamespaceReservationDigest:  value.ExpectedNamespaceReservationDigest,
		ExpectedAuthorizingOrgBindingDigest: value.ExpectedAuthorizingOrgBindingDigest,
		ExpectedPriorSlot: TenantSlotExpectedPrior{
			Variant:         SlotExpectedPriorVariant(value.ExpectedPriorSlot.Variant),
			SlotValueDigest: value.ExpectedPriorSlot.SlotValueDigest,
		},
	}
}

type vectorCatalogSnapshot struct {
	OrgID                 string `json:"org_id"`
	CatalogRevision       string `json:"catalog_revision"`
	CatalogSnapshotDigest string `json:"catalog_snapshot_digest"`
	SchemaGeneration      string `json:"schema_generation"`
	EngineGeneration      string `json:"engine_generation"`
}

type vectorEvidence struct {
	EvidenceVersion                  string                `json:"evidence_version"`
	SourceCatalogSnapshot            vectorCatalogSnapshot `json:"source_catalog_snapshot"`
	BundleKey                        vectorBundleKey       `json:"bundle_key"`
	NamespaceReservationValueVersion string                `json:"namespace_reservation_value_version"`
	NamespaceReservationState        string                `json:"namespace_reservation_state"`
	OrgBindingValueVersion           string                `json:"org_binding_value_version"`
	OrgBindingState                  string                `json:"org_binding_state"`
	SlotValueVersion                 string                `json:"slot_value_version"`
	SlotState                        string                `json:"slot_state"`
}

func (value vectorEvidence) value(t *testing.T) TenantLayoutBundleAuthorityEvidenceV1 {
	t.Helper()
	return TenantLayoutBundleAuthorityEvidenceV1{
		EvidenceVersion: value.EvidenceVersion,
		SourceCatalogSnapshot: catalogpublicationv1.CatalogSnapshotBinding{
			OrgID:                 value.SourceCatalogSnapshot.OrgID,
			CatalogRevision:       parseCanonicalUint(t, "catalog_revision", value.SourceCatalogSnapshot.CatalogRevision),
			CatalogSnapshotDigest: value.SourceCatalogSnapshot.CatalogSnapshotDigest,
			SchemaGeneration:      parseCanonicalUint(t, "schema_generation", value.SourceCatalogSnapshot.SchemaGeneration),
			EngineGeneration:      parseCanonicalUint(t, "engine_generation", value.SourceCatalogSnapshot.EngineGeneration),
		},
		BundleKey:                        value.BundleKey.value(),
		NamespaceReservationValueVersion: value.NamespaceReservationValueVersion,
		NamespaceReservationState:        NamespaceState(value.NamespaceReservationState),
		OrgBindingValueVersion:           value.OrgBindingValueVersion,
		OrgBindingState:                  OrgBindingState(value.OrgBindingState),
		SlotValueVersion:                 value.SlotValueVersion,
		SlotState:                        SlotState(value.SlotState),
	}
}
