package applayoutbundleauthorityv1

import (
	"fmt"

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

// ValidateEvidence validates the closed evidence value without consulting any
// registry or mutable Catalog state. Only a Catalog-owned verifier can prove
// that these exact bytes were derived from the named installation, release,
// reservation, definition version, and source candidate.
func ValidateEvidence(evidence Evidence) error {
	if evidence.EvidenceVersion != EvidenceVersion {
		return fmt.Errorf("Evidence.EvidenceVersion must equal %q", EvidenceVersion)
	}
	if err := catalogpublicationv1.ValidateCatalogSnapshotBinding(evidence.SourceCatalogSnapshot); err != nil {
		return fmt.Errorf("Evidence.SourceCatalogSnapshot: %w", err)
	}
	if err := ValidateBundleKey(evidence.BundleKey); err != nil {
		return fmt.Errorf("Evidence.BundleKey: %w", err)
	}
	if err := appauthority.ValidateAppInstallationID(evidence.AppInstallationID); err != nil {
		return fmt.Errorf("Evidence.AppInstallationID: %w", err)
	}
	if err := appauthority.ValidateDigest(evidence.AppReleaseDigest); err != nil {
		return fmt.Errorf("Evidence.AppReleaseDigest: %w", err)
	}
	if err := appauthority.ValidateDigest(evidence.DeclarationComponentID); err != nil {
		return fmt.Errorf("Evidence.DeclarationComponentID: %w", err)
	}
	if evidence.DefinitionVersion == 0 {
		return fmt.Errorf("Evidence.DefinitionVersion must be positive")
	}
	if err := appauthority.ValidateDigest(evidence.LayoutBundleSlotRevisionDigest); err != nil {
		return fmt.Errorf("Evidence.LayoutBundleSlotRevisionDigest: %w", err)
	}
	return nil
}

// ValidateBundleKey validates the exact versioned App-owned key embedded in
// Evidence. It proves canonical text grammar only, not App registry ownership.
func ValidateBundleKey(key BundleKey) error {
	if key.BundleKeyVersion != layoutbundlekeyv1.BundleKeyVersion {
		return fmt.Errorf("BundleKey.BundleKeyVersion must equal %q", layoutbundlekeyv1.BundleKeyVersion)
	}
	if key.SlotOwnerKind != layoutbundlekeyv1.SlotOwnerKindApp {
		return fmt.Errorf("BundleKey.SlotOwnerKind must equal app")
	}
	portable := portableBundleKey(key)
	if err := layoutbundlekeyv1.ValidateBundleKey(portable); err != nil {
		return err
	}
	// Keep the AppID requirement explicit at this contract boundary. The shared
	// key validator currently performs the same check for App-owned keys, but
	// evidence must not silently weaken if additional key variants are added.
	if err := appauthority.ValidateAppID(key.SlotOwnerNamespace); err != nil {
		return fmt.Errorf("BundleKey.SlotOwnerNamespace: %w", err)
	}
	return nil
}

func portableBundleKey(key BundleKey) layoutbundlekeyv1.BundleKey {
	return layoutbundlekeyv1.BundleKey{
		SlotOwnerKind:      key.SlotOwnerKind,
		SlotOwnerNamespace: key.SlotOwnerNamespace,
		LocalComponentKey:  key.LocalComponentKey,
	}
}
