package catalogsnapshot

import "testing"

func TestRuntimeWorkBindingUsesExactViewIdentity(t *testing.T) {
	input := neutralCompileInput()
	snapshotArtifact, err := CompileCatalogSnapshot(input)
	if err != nil {
		t.Fatal(err)
	}
	snapshot, err := OpenCatalogSnapshot(snapshotArtifact)
	if err != nil {
		t.Fatal(err)
	}
	viewArtifact, err := ProjectRuntimeMetadataView(snapshot, ProjectionKey{
		AppInstallationID: testInstallA, CapabilityID: testCapability,
	})
	if err != nil {
		t.Fatal(err)
	}
	view, err := OpenRuntimeMetadataView(viewArtifact)
	if err != nil {
		t.Fatal(err)
	}
	binding, err := RuntimeWorkBindingFromView(view)
	if err != nil {
		t.Fatal(err)
	}
	if err := binding.VerifyView(view, ProjectionSelectedRootReadV1); err != nil {
		t.Fatalf("verify exact view: %v", err)
	}
	encoded, err := EncodeRuntimeWorkBinding(binding)
	if err != nil {
		t.Fatal(err)
	}
	decoded, err := OpenRuntimeWorkBinding(encoded)
	if err != nil || decoded != binding {
		t.Fatalf("binding round trip = %#v, %v", decoded, err)
	}
	if _, err := OpenRuntimeWorkBinding(encoded + " "); err == nil {
		t.Fatal("non-canonical durable binding accepted")
	}

	changed := binding
	changed.CatalogRevision++
	if err := changed.VerifyView(view, ProjectionSelectedRootReadV1); err == nil {
		t.Fatal("mixed Catalog revision accepted")
	}
	if err := binding.VerifyView(view, ProjectionRecordMutationV1); err == nil {
		t.Fatal("wrong projection profile accepted")
	}
}

func TestRuntimeWorkBindingSetRequiresOneImmutableCatalogLineage(t *testing.T) {
	primary := RuntimeWorkBinding{
		OrgID: "org-1", AppInstallationID: "installation-1",
		RuntimeCapabilityID: "runtime.records.mutate.v1",
		CatalogRevision:     7, SchemaGeneration: 3, EngineGeneration: 2,
		CatalogSnapshotDigest:     ComputeDigest([]byte("snapshot")),
		RuntimeMetadataViewDigest: ComputeDigest([]byte("mutation-view")),
	}
	flow := primary
	flow.RuntimeCapabilityID = "runtime.flow.execute.v1"
	flow.RuntimeMetadataViewDigest = ComputeDigest([]byte("flow-view"))

	encoded, err := EncodeRuntimeWorkBindingSet(RuntimeWorkBindingSet{
		Primary: primary, FlowWork: &flow,
	})
	if err != nil {
		t.Fatal(err)
	}
	opened, err := OpenRuntimeWorkBindingSet(encoded)
	if err != nil {
		t.Fatal(err)
	}
	if opened.Primary != primary || opened.FlowWork == nil || *opened.FlowWork != flow {
		t.Fatalf("binding set round trip = %#v", opened)
	}
	if _, err := OpenRuntimeWorkBindingSet(encoded + " "); err == nil {
		t.Fatal("non-canonical binding set accepted")
	}

	single, err := EncodeRuntimeWorkBinding(primary)
	if err != nil {
		t.Fatal(err)
	}
	openedSingle, err := OpenRuntimeWorkBindingSet(single)
	if err != nil || openedSingle.Primary != primary || openedSingle.FlowWork != nil {
		t.Fatalf("N-1 binding compatibility = %#v, %v", openedSingle, err)
	}

	for _, mutate := range []func(*RuntimeWorkBinding){
		func(binding *RuntimeWorkBinding) { binding.AppInstallationID = "installation-2" },
		func(binding *RuntimeWorkBinding) { binding.CatalogRevision++ },
		func(binding *RuntimeWorkBinding) {
			binding.CatalogSnapshotDigest = ComputeDigest([]byte("other-snapshot"))
		},
	} {
		mixed := flow
		mutate(&mixed)
		if _, err := EncodeRuntimeWorkBindingSet(RuntimeWorkBindingSet{
			Primary: primary, FlowWork: &mixed,
		}); err == nil {
			t.Fatal("mixed immutable Catalog lineage accepted")
		}
	}
}
