package applayoutbundleauthorityv1

import (
	"bytes"
	"encoding/binary"

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

// CanonicalEvidenceBytes returns the complete v1 cross-language evidence
// grammar. Strings are minimal unsigned-LEB128 UTF-8 byte length followed by
// exact bytes; unsigned integers are minimal unsigned LEB128; App owner kind is
// the single byte 0x01. Invalid or non-canonical input is rejected, not fixed.
func CanonicalEvidenceBytes(evidence Evidence) ([]byte, error) {
	if err := ValidateEvidence(evidence); err != nil {
		return nil, err
	}

	bundleKeyBytes, err := layoutbundlekeyv1.CanonicalBundleKeyBytes(portableBundleKey(evidence.BundleKey))
	if err != nil {
		return nil, err
	}

	var out bytes.Buffer
	writeCanonicalString(&out, evidence.EvidenceVersion)
	writeCanonicalString(&out, evidence.SourceCatalogSnapshot.OrgID)
	writeCanonicalUint(&out, evidence.SourceCatalogSnapshot.CatalogRevision)
	writeCanonicalString(&out, evidence.SourceCatalogSnapshot.CatalogSnapshotDigest)
	writeCanonicalUint(&out, evidence.SourceCatalogSnapshot.SchemaGeneration)
	writeCanonicalUint(&out, evidence.SourceCatalogSnapshot.EngineGeneration)
	// CanonicalBundleKeyBytes is exactly S(BundleKeyVersion), owner-kind byte,
	// S(owner namespace), and S(local component key).
	out.Write(bundleKeyBytes)
	writeCanonicalString(&out, evidence.AppInstallationID)
	writeCanonicalString(&out, evidence.AppReleaseDigest)
	writeCanonicalString(&out, evidence.DeclarationComponentID)
	writeCanonicalUint(&out, evidence.DefinitionVersion)
	writeCanonicalString(&out, evidence.LayoutBundleSlotRevisionDigest)
	return out.Bytes(), nil
}

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

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