package catalogreconcile

import (
	"context"
	"errors"
	"testing"

	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/nexuscrm/shared/pkg/models"
)

type memoryStore struct {
	artifacts []catalogsnapshot.Artifact
	failAt    int
}

func (s *memoryStore) Put(_ context.Context, artifact catalogsnapshot.Artifact) error {
	if s.failAt > 0 && len(s.artifacts)+1 == s.failAt {
		return errors.New("store unavailable")
	}
	s.artifacts = append(s.artifacts, artifact)
	return nil
}

func (s *memoryStore) Get(_ context.Context, kind catalogsnapshot.ArtifactKind, digest catalogsnapshot.Digest) (catalogsnapshot.Artifact, error) {
	for _, artifact := range s.artifacts {
		if artifact.Kind() == kind && artifact.Digest() == digest {
			return artifact, nil
		}
	}
	return catalogsnapshot.Artifact{}, catalogsnapshot.ErrArtifactNotFound
}

func TestBuildAndStoreArtifactsClosesTheContentAddressedChain(t *testing.T) {
	effective, selection := fixtureEffectiveMetadata(t)
	store := &memoryStore{}
	result, err := BuildAndStoreArtifacts(context.Background(), store, effective, selection)
	if err != nil {
		t.Fatalf("build and store artifacts: %v", err)
	}
	if len(store.artifacts) != 3 {
		t.Fatalf("stored artifacts = %d, want snapshot, view, receipt", len(store.artifacts))
	}
	wantKinds := []catalogsnapshot.ArtifactKind{
		catalogsnapshot.ArtifactCatalogSnapshot,
		catalogsnapshot.ArtifactRuntimeMetadataView,
		catalogsnapshot.ArtifactServingCompatibilityReceipt,
	}
	for i, kind := range wantKinds {
		if store.artifacts[i].Kind() != kind {
			t.Fatalf("artifact[%d] = %q, want %q", i, store.artifacts[i].Kind(), kind)
		}
	}
	if result.CatalogSnapshotDigest != store.artifacts[0].Digest() ||
		result.RuntimeMetadataViewDigest != store.artifacts[1].Digest() ||
		result.CompatibilityReceiptDigest != store.artifacts[2].Digest() {
		t.Fatalf("result does not name the stored immutable chain: %#v", result)
	}
	secondStore := &memoryStore{}
	second, err := BuildAndStoreArtifacts(context.Background(), secondStore, effective, selection)
	if err != nil || second != result {
		t.Fatalf("deterministic replay = %#v, %v; want %#v", second, err, result)
	}
}

func TestBuildAndStoreArtifactsFailsBeforeSelectionWhenDurabilityFails(t *testing.T) {
	effective, selection := fixtureEffectiveMetadata(t)
	store := &memoryStore{failAt: 2}
	if _, err := BuildAndStoreArtifacts(context.Background(), store, effective, selection); err == nil {
		t.Fatal("partial artifact durability was accepted")
	}
	if len(store.artifacts) != 1 || store.artifacts[0].Kind() != catalogsnapshot.ArtifactCatalogSnapshot {
		t.Fatalf("durable prefix = %#v", store.artifacts)
	}
}

func fixtureEffectiveMetadata(t *testing.T) (catalogsnapshot.EffectiveMetadata, ArtifactSelection) {
	t.Helper()
	metadata := MetadataSelection{
		OrgID: "org-test", AppInstallationID: "installation-test",
		RuntimeCapabilityID: "runtime.records.evaluate-access.v1",
		CatalogRevision:     7, SchemaGeneration: 8, EngineGeneration: 9,
	}
	reader := &markedReader{t: t, objects: []*models.ObjectMetadata{validObject("WorkItem")}}
	effective, err := ReadEffectiveMetadata(context.Background(), func(ctx context.Context, fn func(context.Context) error) error {
		return fn(context.WithValue(ctx, transactionMarker{}, "one-snapshot"))
	}, reader, metadata, nil)
	if err != nil {
		t.Fatalf("build fixture effective metadata: %v", err)
	}
	return effective, ArtifactSelection{
		Metadata:             metadata,
		AppReleaseDigest:     catalogsnapshot.Digest("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
		AppCapabilityID:      "io.nexuscrm.sfcrm.ui.record-access.v1",
		WorkloadID:           "workload-sfcrm-test",
		ServiceReleaseDigest: catalogsnapshot.Digest("sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
		OwnershipEpoch:       1,
		SecurityEpoch:        11,
	}
}
