package appauthority

import "fmt"

type Package struct {
	Manifest       AppManifest
	Bundle         MetadataBundle
	ComponentBlobs []ComponentBlob
}

// ComponentBlob supplies the canonical behavior bytes referenced by one
// MetadataBundle component. The blob is delivery content, not another
// manifest or release identity.
type ComponentBlob struct {
	ComponentID       string
	CanonicalBehavior []byte
}

type VerifiedPackage struct {
	Package
	CanonicalManifest []byte
	CanonicalBundle   []byte
	AppReleaseDigest  string
}

func VerifyPackage(pkg Package) (VerifiedPackage, error) {
	if err := pkg.Bundle.Validate(pkg.Manifest); err != nil {
		return VerifiedPackage{}, err
	}
	if pkg.ComponentBlobs == nil || len(pkg.ComponentBlobs) != len(pkg.Bundle.Components) {
		return VerifiedPackage{}, fmt.Errorf("component blobs must cover every bundle component exactly once")
	}
	for i, component := range pkg.Bundle.Components {
		blob := pkg.ComponentBlobs[i]
		if blob.ComponentID != component.ComponentID {
			return VerifiedPackage{}, fmt.Errorf("component blob[%d] ID %q does not match sorted bundle component %q", i, blob.ComponentID, component.ComponentID)
		}
		digest, err := ComponentDigest(component.ComponentKind, component.ComponentFormatVersion, blob.CanonicalBehavior)
		if err != nil {
			return VerifiedPackage{}, fmt.Errorf("component blob[%d]: %w", i, err)
		}
		if digest != component.ContentDigest {
			return VerifiedPackage{}, fmt.Errorf("component blob[%d] digest %s does not match bundle %s", i, digest, component.ContentDigest)
		}
	}
	manifestBytes, err := CanonicalAppManifest(pkg.Manifest)
	if err != nil {
		return VerifiedPackage{}, err
	}
	bundleBytes, err := CanonicalMetadataBundle(pkg.Bundle, pkg.Manifest)
	if err != nil {
		return VerifiedPackage{}, err
	}
	releaseDigest, err := AppReleaseDigest(pkg.Manifest)
	if err != nil {
		return VerifiedPackage{}, err
	}
	if releaseDigest == pkg.Manifest.MetadataBundleDigest {
		return VerifiedPackage{}, fmt.Errorf("AppRelease digest and MetadataBundle digest must be independent domains")
	}
	return VerifiedPackage{Package: pkg, CanonicalManifest: manifestBytes, CanonicalBundle: bundleBytes, AppReleaseDigest: releaseDigest}, nil
}
