package packs

import "fmt"

// SourceBackedPackIdentity is a packs-minted, non-executable identity for one
// exact SourceBackedPack construction. It exposes neither source state nor an
// install plan.
type SourceBackedPackIdentity struct {
	target       *SourceBackedPack
	construction *ResolvedPackSource
	minted       bool
}

// FreezeSourceBackedPackIdentity captures only the exact constructed target
// and its constructor-owned cloned source pointer.
func FreezeSourceBackedPackIdentity(target *SourceBackedPack) (*SourceBackedPackIdentity, error) {
	if target == nil {
		return nil, fmt.Errorf("freeze source-backed pack identity: target is nil")
	}
	if !target.constructionComplete || target.source == nil || target.manifest == nil {
		return nil, fmt.Errorf("freeze source-backed pack identity: target is incomplete")
	}
	return &SourceBackedPackIdentity{
		target:       target,
		construction: target.source,
		minted:       true,
	}, nil
}

// ValidateSourceBackedPackIdentityTarget confirms that identity still belongs
// to the exact construction at target, including after whole-struct writes to
// the exported target address.
func ValidateSourceBackedPackIdentityTarget(identity *SourceBackedPackIdentity, target *SourceBackedPack) error {
	if err := identity.requireMinted(); err != nil {
		return err
	}
	if target == nil {
		return fmt.Errorf("source-backed pack identity target is nil")
	}
	if identity.target != target || identity.construction != target.source {
		return fmt.Errorf("source-backed pack identity belongs to a different target")
	}
	return nil
}

func (i *SourceBackedPackIdentity) requireMinted() error {
	if i == nil || !i.minted || i.target == nil || i.construction == nil {
		return fmt.Errorf("source-backed pack identity was not minted by packs")
	}
	return nil
}
