package servingbindingv1

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
)

const canonicalBindingDomain = "nexuscrm.catalog-serving-binding/v1"

// CanonicalBytes returns the versioned, transport-independent identity of
// binding. The enclosing Catalog serving receipt remains the content-addressed
// serving artifact; this function supplies a stable value digest for wire and
// cross-version verification without creating another read path.
func CanonicalBytes(binding Binding) ([]byte, error) {
	if err := Validate(binding); err != nil {
		return nil, err
	}
	var out bytes.Buffer
	writeString(&out, canonicalBindingDomain)
	writeString(&out, binding.OrgID)
	writeString(&out, binding.AppInstallationID)
	writeString(&out, binding.AppReleaseDigest)
	writeString(&out, binding.WorkloadID)
	writeString(&out, binding.CanonicalResource)
	writeString(&out, binding.ToolContractDigest)
	writeString(&out, binding.ScopeRouteReceiptDigest)
	writeUint(&out, uint64(len(binding.WorkloadCapabilityCeiling)))
	for _, capability := range binding.WorkloadCapabilityCeiling {
		writeString(&out, capability)
	}
	writeString(&out, binding.Receipts.ProtocolVersion)
	writeUint(&out, binding.Receipts.CatalogRevision)
	writeUint(&out, binding.Receipts.SchemaGeneration)
	writeUint(&out, binding.Receipts.EngineGeneration)
	writeUint(&out, binding.Receipts.SecurityEpoch)
	writeString(&out, binding.Receipts.CapabilityVersion)
	return out.Bytes(), nil
}

// BindingDigest returns the SHA-256 digest of CanonicalBytes(binding).
func BindingDigest(binding Binding) (string, error) {
	canonical, err := CanonicalBytes(binding)
	if err != nil {
		return "", err
	}
	sum := sha256.Sum256(canonical)
	return "sha256:" + hex.EncodeToString(sum[:]), nil
}

func writeString(out *bytes.Buffer, value string) {
	writeUint(out, uint64(len(value)))
	out.WriteString(value)
}

func writeUint(out *bytes.Buffer, value uint64) {
	var encoded [binary.MaxVarintLen64]byte
	n := binary.PutUvarint(encoded[:], value)
	out.Write(encoded[:n])
}
