package bindingowner

import (
	"bytes"
	"context"
	"database/sql"
	"errors"
	"fmt"
	"sort"
	"strconv"

	"github.com/pingcap-inc/nexuscrm-connectors/internal/sqltx"
	catalogv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	connectorbindingownerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/connectorbindingownerv1"
)

// Repository is the Connector-owned read path for immutable binding-owner
// definitions. It deliberately exposes no list, current/latest, write, repair,
// or mutable-source API.
type Repository struct {
	db *sql.DB
}

// ExactBindingSelection is one immutable request for bindings from one exact
// PackGeneration. Its fields remain private so a validated selection cannot be
// changed between aggregate preflight and the locking read.
type ExactBindingSelection struct {
	coordinate PackGenerationCoordinate
	bindings   []catalogv1.IntegrationBinding
}

// NewExactBindingSelection validates and detaches one generation-scoped
// selection. An explicit empty binding slice is valid; nil is not.
func NewExactBindingSelection(
	coordinate PackGenerationCoordinate,
	bindings []catalogv1.IntegrationBinding,
) (ExactBindingSelection, error) {
	if err := validateCoordinate(coordinate); err != nil {
		return ExactBindingSelection{}, err
	}
	if _, err := validateRequestedBindings(coordinate, bindings); err != nil {
		return ExactBindingSelection{}, err
	}
	return ExactBindingSelection{
		coordinate: coordinate,
		bindings:   cloneIntegrationBindings(bindings),
	}, nil
}

// NewRepository constructs an exact binding-owner reader over the supplied
// TiDB pool.
func NewRepository(db *sql.DB) (*Repository, error) {
	if db == nil {
		return nil, fmt.Errorf("%w: database is nil", ErrDependencyMissing)
	}
	return &Repository{db: db}, nil
}

// ResolveExact resolves one binding through ResolveExactBindings' sole
// generation-scoped exact-read path.
func (repository *Repository) ResolveExact(
	ctx context.Context,
	coordinate PackGenerationCoordinate,
	binding catalogv1.IntegrationBinding,
) (connectorbindingownerv1.DefinitionEnvelope, error) {
	definitions, err := repository.ResolveExactBindings(
		ctx,
		coordinate,
		[]catalogv1.IntegrationBinding{binding},
	)
	if err != nil {
		return connectorbindingownerv1.DefinitionEnvelope{}, err
	}
	return definitions[0], nil
}

// ResolveExactBindings locks and validates the exact immutable pack-generation
// parent, definition-set header, and every definition child once before
// selecting every requested binding in caller order. The returned envelopes are
// detached and cross the repository boundary only after the read transaction
// commits successfully.
//
// This is not a list or discovery API. Bindings must be a non-nil, bounded set
// of unique natural identities that all name the supplied exact generation. An
// explicit empty selection returns an allocated empty result without opening a
// transaction.
func (repository *Repository) ResolveExactBindings(
	ctx context.Context,
	coordinate PackGenerationCoordinate,
	bindings []catalogv1.IntegrationBinding,
) ([]connectorbindingownerv1.DefinitionEnvelope, error) {
	if repository == nil || repository.db == nil {
		return nil, fmt.Errorf(
			"%w: repository is unavailable",
			ErrDependencyMissing,
		)
	}
	if ctx == nil {
		return nil, fmt.Errorf(
			"%w: context is nil",
			ErrInvalidRequest,
		)
	}
	selection, err := NewExactBindingSelection(coordinate, bindings)
	if err != nil {
		return nil, err
	}
	resolved, err := repository.resolveExactBindingSelections(
		ctx,
		[]ExactBindingSelection{selection},
		connectorbindingownerv1.MaxDefinitionSetBytes,
	)
	if err != nil {
		return nil, err
	}
	return resolved[0], nil
}

// ResolveExactBindingSelections locks and validates multiple complete
// immutable PackGeneration definition sets in deterministic storage-ID order
// within one transaction. The result preserves caller selection and binding
// order. The sum of every scanned canonical DefinitionEnvelope JSON byte over
// all generations is bounded by connectorbindingownerv1.MaxDefinitionSetBytes;
// a conflict or budget failure discards the whole result.
func (repository *Repository) ResolveExactBindingSelections(
	ctx context.Context,
	selections []ExactBindingSelection,
) ([][]connectorbindingownerv1.DefinitionEnvelope, error) {
	if repository == nil || repository.db == nil {
		return nil, fmt.Errorf(
			"%w: repository is unavailable",
			ErrDependencyMissing,
		)
	}
	if ctx == nil {
		return nil, fmt.Errorf(
			"%w: context is nil",
			ErrInvalidRequest,
		)
	}
	return repository.resolveExactBindingSelections(
		ctx,
		selections,
		connectorbindingownerv1.MaxDefinitionSetBytes,
	)
}

type validatedExactBindingSelection struct {
	originalIndex int
	coordinate    PackGenerationCoordinate
	bindings      []catalogv1.IntegrationBinding
	identities    []connectorbindingownerv1.Identity
}

type portablePackGenerationCoordinate struct {
	ownerPack          string
	packInstallationID string
	packPayloadDigest  string
}

func (repository *Repository) resolveExactBindingSelections(
	ctx context.Context,
	selections []ExactBindingSelection,
	maxDefinitionBytes uint64,
) ([][]connectorbindingownerv1.DefinitionEnvelope, error) {
	validated, err := validateExactBindingSelections(selections)
	if err != nil {
		return nil, err
	}
	resolved := make([][]connectorbindingownerv1.DefinitionEnvelope, len(validated))
	nonEmpty := make([]int, 0, len(validated))
	for index := range validated {
		resolved[index] = make([]connectorbindingownerv1.DefinitionEnvelope, len(validated[index].bindings))
		if len(validated[index].bindings) != 0 {
			nonEmpty = append(nonEmpty, index)
		}
	}
	if len(nonEmpty) == 0 {
		return resolved, nil
	}
	sort.Slice(nonEmpty, func(left, right int) bool {
		return validated[nonEmpty[left]].coordinate.StorageID <
			validated[nonEmpty[right]].coordinate.StorageID
	})

	tx, cleanup, err := sqltx.BeginPessimisticTx(ctx, repository.db)
	if err != nil {
		return nil, fmt.Errorf("begin exact binding-owner selection read: %w", err)
	}
	rollback := func(cause error) ([][]connectorbindingownerv1.DefinitionEnvelope, error) {
		return nil, errors.Join(cause, tx.Rollback(), cleanup())
	}

	var definitionBytes uint64
	for _, validatedIndex := range nonEmpty {
		selection := validated[validatedIndex]
		definitionSet, setBytes, err := readVerifiedDefinitionSet(
			ctx,
			tx,
			selection.coordinate,
			maxDefinitionBytes-definitionBytes,
		)
		if err != nil {
			return rollback(err)
		}
		definitionBytes += setBytes
		definitions, err := connectorbindingownerv1.ValidateDefinitionSet(definitionSet)
		if err != nil {
			return rollback(fmt.Errorf(
				"%w: pack %q definition set is malformed: %v",
				ErrAuthorityConflict,
				selection.coordinate.OwnerPack,
				err,
			))
		}
		selected, err := selectExactBindingDefinitions(selection, definitions)
		if err != nil {
			return rollback(err)
		}
		resolved[selection.originalIndex] = selected
	}
	if err := errors.Join(tx.Commit(), cleanup()); err != nil {
		return nil, fmt.Errorf("commit exact binding-owner selection read: %w", err)
	}
	return resolved, nil
}

func validateExactBindingSelections(
	selections []ExactBindingSelection,
) ([]validatedExactBindingSelection, error) {
	if selections == nil {
		return nil, fmt.Errorf("%w: exact binding selections are nil", ErrInvalidRequest)
	}
	if len(selections) > connectorbindingownerv1.MaxDefinitionCount {
		return nil, fmt.Errorf(
			"%w: exact binding selection count %d exceeds %d",
			ErrInvalidRequest,
			len(selections),
			connectorbindingownerv1.MaxDefinitionCount,
		)
	}
	validated := make([]validatedExactBindingSelection, len(selections))
	byStorageID := make(map[string]portablePackGenerationCoordinate, len(selections))
	byPortableCoordinate := make(map[portablePackGenerationCoordinate]string, len(selections))
	totalBindings := 0
	for index, selection := range selections {
		if err := validateCoordinate(selection.coordinate); err != nil {
			return nil, fmt.Errorf("exact binding selection %d: %w", index, err)
		}
		identities, err := validateRequestedBindings(selection.coordinate, selection.bindings)
		if err != nil {
			return nil, fmt.Errorf("exact binding selection %d: %w", index, err)
		}
		if len(selection.bindings) > catalogv1.MaxBindingCount-totalBindings {
			return nil, fmt.Errorf(
				"%w: exact binding selections exceed %d total bindings",
				ErrInvalidRequest,
				catalogv1.MaxBindingCount,
			)
		}
		totalBindings += len(selection.bindings)
		portable := portablePackGenerationCoordinate{
			ownerPack:          selection.coordinate.OwnerPack,
			packInstallationID: selection.coordinate.PackInstallationID,
			packPayloadDigest:  selection.coordinate.PackPayloadDigest,
		}
		if _, duplicate := byStorageID[selection.coordinate.StorageID]; duplicate {
			return nil, fmt.Errorf(
				"%w: exact binding selections repeat PackGeneration storage ID %q",
				ErrInvalidRequest,
				selection.coordinate.StorageID,
			)
		}
		if previousStorageID, duplicate := byPortableCoordinate[portable]; duplicate {
			return nil, fmt.Errorf(
				"%w: one portable PackGeneration coordinate names storage IDs %q and %q",
				ErrInvalidRequest,
				previousStorageID,
				selection.coordinate.StorageID,
			)
		}
		byStorageID[selection.coordinate.StorageID] = portable
		byPortableCoordinate[portable] = selection.coordinate.StorageID
		validated[index] = validatedExactBindingSelection{
			originalIndex: index,
			coordinate:    selection.coordinate,
			bindings:      cloneIntegrationBindings(selection.bindings),
			identities:    append([]connectorbindingownerv1.Identity(nil), identities...),
		}
	}
	return validated, nil
}

func selectExactBindingDefinitions(
	selection validatedExactBindingSelection,
	definitions []connectorbindingownerv1.DefinitionEnvelope,
) ([]connectorbindingownerv1.DefinitionEnvelope, error) {
	byNaturalIdentity := make(map[bindingNaturalIdentity]connectorbindingownerv1.DefinitionEnvelope, len(definitions))
	for _, definition := range definitions {
		key := bindingNaturalIdentity{kind: definition.Kind, businessKey: definition.BusinessKey}
		if _, duplicate := byNaturalIdentity[key]; duplicate {
			return nil, fmt.Errorf(
				"%w: pack %q definition set repeats binding %s/%s",
				ErrAuthorityConflict,
				selection.coordinate.OwnerPack,
				definition.Kind,
				definition.BusinessKey,
			)
		}
		byNaturalIdentity[key] = definition
	}
	resolved := make([]connectorbindingownerv1.DefinitionEnvelope, len(selection.bindings))
	for index, binding := range selection.bindings {
		definition, found := byNaturalIdentity[bindingNaturalIdentity{
			kind:        binding.Kind,
			businessKey: binding.BusinessKey,
		}]
		if !found {
			return nil, fmt.Errorf(
				"%w: binding %s/%s is absent from its complete definition set",
				ErrAuthorityConflict,
				binding.Kind,
				binding.BusinessKey,
			)
		}
		if definition.Identity != selection.identities[index] {
			return nil, fmt.Errorf(
				"%w: binding %s/%s disagrees with its immutable owner identity",
				ErrAuthorityConflict,
				binding.Kind,
				binding.BusinessKey,
			)
		}
		resolved[index] = connectorbindingownerv1.CloneDefinitionEnvelope(definition)
	}
	return resolved, nil
}

func cloneIntegrationBindings(bindings []catalogv1.IntegrationBinding) []catalogv1.IntegrationBinding {
	if bindings == nil {
		return nil
	}
	cloned := make([]catalogv1.IntegrationBinding, len(bindings))
	for index, binding := range bindings {
		cloned[index] = binding
		cloned[index].CredentialRefs = append([]string(nil), binding.CredentialRefs...)
		if binding.RuntimeCommand != nil {
			runtimeCommand := *binding.RuntimeCommand
			cloned[index].RuntimeCommand = &runtimeCommand
		}
	}
	return cloned
}

type bindingNaturalIdentity struct {
	kind        catalogv1.IntegrationBindingKind
	businessKey string
}

func validateRequestedBindings(
	coordinate PackGenerationCoordinate,
	bindings []catalogv1.IntegrationBinding,
) ([]connectorbindingownerv1.Identity, error) {
	if bindings == nil {
		return nil, fmt.Errorf("%w: binding selection is nil", ErrInvalidRequest)
	}
	if len(bindings) > connectorbindingownerv1.MaxDefinitionCount {
		return nil, fmt.Errorf(
			"%w: binding selection count %d exceeds %d",
			ErrInvalidRequest,
			len(bindings),
			connectorbindingownerv1.MaxDefinitionCount,
		)
	}
	identities := make([]connectorbindingownerv1.Identity, len(bindings))
	seen := make(map[bindingNaturalIdentity]struct{}, len(bindings))
	for index, binding := range bindings {
		identity := connectorbindingownerv1.IdentityFromBinding(binding)
		if err := connectorbindingownerv1.ValidateIdentity(identity); err != nil {
			return nil, fmt.Errorf(
				"%w: binding identity at index %d is malformed: %v",
				ErrInvalidRequest,
				index,
				err,
			)
		}
		if identity.OwnerPack != coordinate.OwnerPack ||
			identity.PackInstallationID != coordinate.PackInstallationID ||
			identity.PackPayloadDigest != coordinate.PackPayloadDigest {
			return nil, fmt.Errorf(
				"%w: binding %s/%s names a different pack generation",
				ErrAuthorityConflict,
				binding.Kind,
				binding.BusinessKey,
			)
		}
		key := bindingNaturalIdentity{kind: identity.Kind, businessKey: identity.BusinessKey}
		if _, duplicate := seen[key]; duplicate {
			return nil, fmt.Errorf(
				"%w: binding selection repeats %s/%s",
				ErrInvalidRequest,
				binding.Kind,
				binding.BusinessKey,
			)
		}
		seen[key] = struct{}{}
		identities[index] = identity
	}
	return identities, nil
}

func validateCoordinate(coordinate PackGenerationCoordinate) error {
	portable := connectorbindingownerv1.DefinitionSetCoordinate{
		OwnerPack:          coordinate.OwnerPack,
		PackInstallationID: coordinate.PackInstallationID,
		PackPayloadDigest:  coordinate.PackPayloadDigest,
	}
	if _, err := connectorbindingownerv1.NewDefinitionSet(portable, nil); err != nil {
		return fmt.Errorf("%w: pack-generation coordinate is malformed: %v", ErrInvalidRequest, err)
	}
	if coordinate.StorageID == "" {
		return fmt.Errorf("%w: pack-generation storage identity is required", ErrInvalidRequest)
	}
	if len(coordinate.StorageID) > connectorbindingownerv1.MaxIdentifierBytes {
		return fmt.Errorf(
			"%w: pack-generation storage identity exceeds %d bytes",
			ErrInvalidRequest,
			connectorbindingownerv1.MaxIdentifierBytes,
		)
	}
	return nil
}

func readVerifiedDefinitionSet(
	ctx context.Context,
	tx *sql.Tx,
	coordinate PackGenerationCoordinate,
	aggregateRemaining uint64,
) (connectorbindingownerv1.DefinitionSet, uint64, error) {
	if err := lockAndVerifyPackGeneration(ctx, tx, coordinate); err != nil {
		return connectorbindingownerv1.DefinitionSet{}, 0, err
	}

	var (
		storedGenerationID []byte
		protocol           []byte
		rawCount           []byte
		setDigest          []byte
	)
	err := tx.QueryRowContext(ctx, `SELECT
PackGenerationId, Protocol, DefinitionCount, DefinitionSetDigest
FROM _System_PackGenerationConnectorBindingDefinitionSet
WHERE PackGenerationId = ?
  AND BINARY PackGenerationId = BINARY ?
FOR UPDATE`, coordinate.StorageID, coordinate.StorageID).Scan(
		&storedGenerationID,
		&protocol,
		&rawCount,
		&setDigest,
	)
	if errors.Is(err, sql.ErrNoRows) {
		return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
			"%w: pack %q generation is missing its definition-set header",
			ErrAuthorityConflict,
			coordinate.OwnerPack,
		)
	}
	if err != nil {
		return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
			"lock pack %q definition-set header: %w",
			coordinate.OwnerPack,
			err,
		)
	}
	if !physicalStringEqual(storedGenerationID, coordinate.StorageID) {
		return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
			"%w: pack %q definition-set header names a different generation",
			ErrAuthorityConflict,
			coordinate.OwnerPack,
		)
	}
	definitionCount, err := parseCanonicalUint64(rawCount, true)
	if err != nil {
		return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
			"%w: pack %q definition count is malformed: %v",
			ErrAuthorityConflict,
			coordinate.OwnerPack,
			err,
		)
	}
	if definitionCount > connectorbindingownerv1.MaxDefinitionCount {
		return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
			"%w: pack %q definition count exceeds %d",
			ErrAuthorityConflict,
			coordinate.OwnerPack,
			connectorbindingownerv1.MaxDefinitionCount,
		)
	}

	rows, err := tx.QueryContext(ctx, `SELECT
PackGenerationId, PackInstallId, DefinitionOrdinal, Kind, OwnerPack,
PackPayloadDigest, BusinessKey, DefinitionVersion, DefinitionEnvelopeDigest,
DefinitionEnvelope
FROM _System_PackGenerationConnectorBindingDefinition
WHERE PackGenerationId = ?
ORDER BY DefinitionOrdinal
FOR UPDATE`, coordinate.StorageID)
	if err != nil {
		return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
			"lock pack %q definition children: %w",
			coordinate.OwnerPack,
			err,
		)
	}
	entries := make([]connectorbindingownerv1.DefinitionSetEntry, 0, int(definitionCount))
	var definitionBytes uint64
	for rows.Next() {
		if len(entries) >= connectorbindingownerv1.MaxDefinitionCount {
			_ = rows.Close()
			return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
				"%w: pack %q definition rows exceed %d",
				ErrAuthorityConflict,
				coordinate.OwnerPack,
				connectorbindingownerv1.MaxDefinitionCount,
			)
		}
		var (
			packGenerationID   []byte
			packInstallationID []byte
			rawOrdinal         []byte
			rawKind            []byte
			ownerPack          []byte
			packPayloadDigest  []byte
			businessKey        []byte
			rawVersion         []byte
			envelopeDigest     []byte
			envelope           sql.RawBytes
		)
		if err := rows.Scan(
			&packGenerationID,
			&packInstallationID,
			&rawOrdinal,
			&rawKind,
			&ownerPack,
			&packPayloadDigest,
			&businessKey,
			&rawVersion,
			&envelopeDigest,
			&envelope,
		); err != nil {
			_ = rows.Close()
			return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
				"scan pack %q definition child: %w",
				coordinate.OwnerPack,
				err,
			)
		}
		if !physicalStringEqual(packGenerationID, coordinate.StorageID) {
			_ = rows.Close()
			return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
				"%w: pack %q definition child names a byte-distinct generation",
				ErrAuthorityConflict,
				coordinate.OwnerPack,
			)
		}
		ordinal, err := parseCanonicalUint64(rawOrdinal, true)
		if err != nil {
			_ = rows.Close()
			return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
				"%w: pack %q definition ordinal is malformed: %v",
				ErrAuthorityConflict,
				coordinate.OwnerPack,
				err,
			)
		}
		version, err := parseCanonicalUint64(rawVersion, false)
		if err != nil {
			_ = rows.Close()
			return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
				"%w: pack %q definition version is malformed: %v",
				ErrAuthorityConflict,
				coordinate.OwnerPack,
				err,
			)
		}
		if uint64(len(envelope)) > connectorbindingownerv1.MaxSerializedEnvelopeBytes ||
			definitionBytes > connectorbindingownerv1.MaxDefinitionSetBytes ||
			uint64(len(envelope)) > connectorbindingownerv1.MaxDefinitionSetBytes-definitionBytes {
			_ = rows.Close()
			return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
				"%w: pack %q definition bytes exceed their closed budget",
				ErrAuthorityConflict,
				coordinate.OwnerPack,
			)
		}
		if definitionBytes > aggregateRemaining ||
			uint64(len(envelope)) > aggregateRemaining-definitionBytes {
			_ = rows.Close()
			return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
				"%w: complete definition-set bytes exceed the remaining aggregate budget of %d bytes",
				ErrExactReadBudgetExceeded,
				aggregateRemaining,
			)
		}
		definitionBytes += uint64(len(envelope))
		entry := connectorbindingownerv1.DefinitionSetEntry{
			Ordinal:            ordinal,
			Kind:               catalogv1.IntegrationBindingKind(string(rawKind)),
			OwnerPack:          string(ownerPack),
			PackInstallationID: string(packInstallationID),
			PackPayloadDigest:  string(packPayloadDigest),
			BusinessKey:        string(businessKey),
			DefinitionVersion:  version,
			EnvelopeJSON:       append([]byte(nil), envelope...),
			EnvelopeDigest:     string(envelopeDigest),
		}
		entries = append(entries, entry)
	}
	if err := rows.Err(); err != nil {
		_ = rows.Close()
		return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
			"iterate pack %q definition children: %w",
			coordinate.OwnerPack,
			err,
		)
	}
	if err := rows.Close(); err != nil {
		return connectorbindingownerv1.DefinitionSet{}, 0, fmt.Errorf(
			"close pack %q definition children: %w",
			coordinate.OwnerPack,
			err,
		)
	}

	return connectorbindingownerv1.DefinitionSet{
		Header: connectorbindingownerv1.DefinitionSetHeader{
			Protocol: string(protocol),
			DefinitionSetCoordinate: connectorbindingownerv1.DefinitionSetCoordinate{
				OwnerPack:          coordinate.OwnerPack,
				PackInstallationID: coordinate.PackInstallationID,
				PackPayloadDigest:  coordinate.PackPayloadDigest,
			},
			DefinitionCount:  definitionCount,
			OrderedSetDigest: string(setDigest),
		},
		Entries: entries,
	}, definitionBytes, nil
}

func lockAndVerifyPackGeneration(
	ctx context.Context,
	tx *sql.Tx,
	coordinate PackGenerationCoordinate,
) error {
	var (
		storageID          []byte
		ownerPack          []byte
		packInstallationID []byte
		packPayloadDigest  sql.NullString
	)
	err := tx.QueryRowContext(ctx, `SELECT
Id, PackName, PackInstallId, AppliedPayloadHash
FROM _System_PackGeneration
WHERE Id = ?
  AND BINARY Id = BINARY ?
LIMIT 1
FOR UPDATE`, coordinate.StorageID, coordinate.StorageID).Scan(
		&storageID,
		&ownerPack,
		&packInstallationID,
		&packPayloadDigest,
	)
	if errors.Is(err, sql.ErrNoRows) {
		return fmt.Errorf(
			"%w: pack %q generation is missing its immutable parent",
			ErrAuthorityConflict,
			coordinate.OwnerPack,
		)
	}
	if err != nil {
		return fmt.Errorf("lock pack %q immutable parent: %w", coordinate.OwnerPack, err)
	}
	if !physicalStringEqual(storageID, coordinate.StorageID) ||
		!physicalStringEqual(ownerPack, coordinate.OwnerPack) ||
		!physicalStringEqual(packInstallationID, coordinate.PackInstallationID) ||
		!packPayloadDigest.Valid || packPayloadDigest.String != coordinate.PackPayloadDigest {
		return fmt.Errorf(
			"%w: pack %q immutable parent disagrees with its exact coordinate",
			ErrAuthorityConflict,
			coordinate.OwnerPack,
		)
	}
	return nil
}

func parseCanonicalUint64(raw []byte, allowZero bool) (uint64, error) {
	if len(raw) == 0 {
		return 0, fmt.Errorf("value is empty")
	}
	value, err := strconv.ParseUint(string(raw), 10, 64)
	if err != nil || strconv.FormatUint(value, 10) != string(raw) {
		return 0, fmt.Errorf("value is not a canonical unsigned decimal")
	}
	if !allowZero && value == 0 {
		return 0, fmt.Errorf("value must be positive")
	}
	return value, nil
}

func physicalStringEqual(raw []byte, expected string) bool {
	return bytes.Equal(raw, []byte(expected))
}
