package servingbindingv1

import (
	"errors"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/pingcap-inc/nexuscrm-contract/pkg/appauthority"
	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/delegationv1"
)

// Validate fails closed unless binding carries every exact serving identity
// axis. CanonicalResource is deliberately not normalized here: the existing
// authoritative resource parser owns normalization, while this immutable
// value preserves and compares its resulting canonical bytes exactly.
func Validate(binding Binding) error {
	if !validOpaque(binding.OrgID, MaxOrgIDBytes) {
		return errors.New("catalog serving binding: invalid OrgID")
	}
	if err := appauthority.ValidateAppInstallationID(binding.AppInstallationID); err != nil {
		return errors.New("catalog serving binding: invalid AppInstallationID")
	}
	if err := appauthority.ValidateDigest(binding.AppReleaseDigest); err != nil {
		return errors.New("catalog serving binding: invalid AppReleaseDigest")
	}
	if !validOpaque(binding.WorkloadID, MaxWorkloadIDBytes) {
		return errors.New("catalog serving binding: invalid WorkloadID")
	}
	if !validOpaque(binding.CanonicalResource, MaxCanonicalResourceBytes) {
		return errors.New("catalog serving binding: invalid CanonicalResource")
	}
	if err := appauthority.ValidateDigest(binding.ToolContractDigest); err != nil {
		return errors.New("catalog serving binding: invalid ToolContractDigest")
	}
	if err := appauthority.ValidateDigest(binding.ScopeRouteReceiptDigest); err != nil {
		return errors.New("catalog serving binding: invalid ScopeRouteReceiptDigest")
	}
	if !validCapabilityCeiling(binding.WorkloadCapabilityCeiling) {
		return errors.New("catalog serving binding: invalid WorkloadCapabilityCeiling")
	}
	if !validReceipts(binding.Receipts) {
		return errors.New("catalog serving binding: invalid receipts")
	}
	return nil
}

// MatchesExact reports whether binding contains exactly the trusted assertion.
// expected is not an authority source: callers must obtain it from verified
// Catalog/receipt state before comparing it here.
func MatchesExact(binding Binding, expected ExpectedBinding) bool {
	return Validate(binding) == nil && validExpected(expected) &&
		binding.OrgID == expected.OrgID &&
		binding.AppInstallationID == expected.AppInstallationID &&
		binding.AppReleaseDigest == expected.AppReleaseDigest &&
		binding.WorkloadID == expected.WorkloadID &&
		binding.CanonicalResource == expected.CanonicalResource &&
		binding.ToolContractDigest == expected.ToolContractDigest &&
		binding.ScopeRouteReceiptDigest == expected.ScopeRouteReceiptDigest
}

// MatchesReceipts reports whether binding was derived from exactly expected
// Catalog, schema, engine, and security generations. Equality intentionally
// rejects stale and downgrade values; ordering/transition policy remains with
// the Catalog and Runtime authorities.
func MatchesReceipts(binding Binding, expected common.Receipts) bool {
	return Validate(binding) == nil && validReceipts(expected) && binding.Receipts == expected
}

func validExpected(expected ExpectedBinding) bool {
	return validOpaque(expected.OrgID, MaxOrgIDBytes) &&
		appauthority.ValidateAppInstallationID(expected.AppInstallationID) == nil &&
		appauthority.ValidateDigest(expected.AppReleaseDigest) == nil &&
		validOpaque(expected.WorkloadID, MaxWorkloadIDBytes) &&
		validOpaque(expected.CanonicalResource, MaxCanonicalResourceBytes) &&
		appauthority.ValidateDigest(expected.ToolContractDigest) == nil &&
		appauthority.ValidateDigest(expected.ScopeRouteReceiptDigest) == nil
}

func validReceipts(receipts common.Receipts) bool {
	return common.ValidReceipts(receipts, CapabilityName) &&
		receipts.CatalogRevision > 0 && receipts.SchemaGeneration > 0 &&
		receipts.EngineGeneration > 0 && receipts.SecurityEpoch > 0
}

func validCapabilityCeiling(capabilities []string) bool {
	if len(capabilities) == 0 {
		return false
	}
	return delegationv1.ValidateCapabilityGrants(capabilities) == nil
}

func validOpaque(value string, maximum int) bool {
	if value == "" || len(value) > maximum || !utf8.ValidString(value) || strings.TrimSpace(value) != value {
		return false
	}
	for _, r := range value {
		if unicode.IsControl(r) {
			return false
		}
	}
	return true
}
