package runtimeapi

import (
	"context"
	"fmt"

	"github.com/nexuscrm/metadata/catalogsnapshot"
)

// servingArtifactSet lives for one preparation attempt only. It shares immutable
// decoding/proof work, never mutable Catalog authority or selection checks.
// Nothing is retained by ServingState between reconciliation attempts.
type servingArtifactSet struct {
	store     catalogsnapshot.Store
	receipts  map[catalogsnapshot.Digest]*catalogsnapshot.ServingCompatibilityReceipt
	snapshots map[catalogsnapshot.Digest]*catalogsnapshot.CatalogSnapshot
	views     map[catalogsnapshot.Digest]*catalogsnapshot.RuntimeMetadataView
	sources   map[servingViewSource]struct{}
}

type servingViewSource struct{ view, snapshot catalogsnapshot.Digest }

func newServingArtifactSet(store catalogsnapshot.Store) *servingArtifactSet {
	return &servingArtifactSet{
		store:     store,
		receipts:  make(map[catalogsnapshot.Digest]*catalogsnapshot.ServingCompatibilityReceipt),
		snapshots: make(map[catalogsnapshot.Digest]*catalogsnapshot.CatalogSnapshot),
		views:     make(map[catalogsnapshot.Digest]*catalogsnapshot.RuntimeMetadataView),
		sources:   make(map[servingViewSource]struct{}),
	}
}

func (a *servingArtifactSet) receipt(ctx context.Context, digest catalogsnapshot.Digest) (*catalogsnapshot.ServingCompatibilityReceipt, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	if receipt := a.receipts[digest]; receipt != nil {
		return receipt, nil
	}
	artifact, err := a.store.Get(ctx, catalogsnapshot.ArtifactServingCompatibilityReceipt, digest)
	if err != nil {
		return nil, err
	}
	receipt, err := catalogsnapshot.OpenServingCompatibilityReceipt(artifact)
	if err != nil {
		return nil, err
	}
	if receipt.Digest() != digest {
		return nil, fmt.Errorf("%w: requested receipt identity mismatch", catalogsnapshot.ErrArtifactCorrupt)
	}
	a.receipts[digest] = receipt
	return receipt, nil
}

func (a *servingArtifactSet) snapshot(ctx context.Context, digest catalogsnapshot.Digest) (*catalogsnapshot.CatalogSnapshot, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	if snapshot := a.snapshots[digest]; snapshot != nil {
		return snapshot, nil
	}
	artifact, err := a.store.Get(ctx, catalogsnapshot.ArtifactCatalogSnapshot, digest)
	if err != nil {
		return nil, err
	}
	snapshot, err := catalogsnapshot.OpenCatalogSnapshot(artifact)
	if err != nil {
		return nil, err
	}
	if snapshot.Digest() != digest {
		return nil, fmt.Errorf("%w: requested snapshot identity mismatch", catalogsnapshot.ErrArtifactCorrupt)
	}
	a.snapshots[digest] = snapshot
	return snapshot, nil
}

func (a *servingArtifactSet) view(ctx context.Context, digest catalogsnapshot.Digest) (*catalogsnapshot.RuntimeMetadataView, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	if view := a.views[digest]; view != nil {
		return view, nil
	}
	artifact, err := a.store.Get(ctx, catalogsnapshot.ArtifactRuntimeMetadataView, digest)
	if err != nil {
		return nil, err
	}
	view, err := catalogsnapshot.OpenRuntimeMetadataView(artifact)
	if err != nil {
		return nil, err
	}
	if view.Digest() != digest {
		return nil, fmt.Errorf("%w: requested view identity mismatch", catalogsnapshot.ErrArtifactCorrupt)
	}
	a.views[digest] = view
	return view, nil
}

func (a *servingArtifactSet) verifySource(view *catalogsnapshot.RuntimeMetadataView, snapshot *catalogsnapshot.CatalogSnapshot) error {
	key := servingViewSource{view.Digest(), snapshot.Digest()}
	if _, verified := a.sources[key]; verified {
		return nil
	}
	if err := view.VerifySource(snapshot); err != nil {
		return err
	}
	a.sources[key] = struct{}{}
	return nil
}
