package packs

import (
	"strings"
	"testing"
)

func TestFreezeSourceBackedPackIdentityRejectsInvalidTargets(t *testing.T) {
	tests := []struct {
		name   string
		target *SourceBackedPack
		want   string
	}{
		{name: "nil", want: "target is nil"},
		{name: "zero", target: &SourceBackedPack{}, want: "target is incomplete"},
		{
			name: "forged complete flag",
			target: &SourceBackedPack{
				constructionComplete: true,
			},
			want: "target is incomplete",
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			identity, err := FreezeSourceBackedPackIdentity(test.target)
			if err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("error = %v, want containing %q", err, test.want)
			}
			if identity != nil {
				t.Fatalf("identity = %#v, want a real nil pointer", identity)
			}
		})
	}
}

func TestSourceBackedPackIdentityRejectsUnmintedValues(t *testing.T) {
	for name, identity := range map[string]*SourceBackedPackIdentity{
		"nil":                     nil,
		"zero":                    {},
		"forged minted flag only": {minted: true},
		"forged target only":      {target: &SourceBackedPack{}, minted: true},
	} {
		t.Run(name, func(t *testing.T) {
			if err := ValidateSourceBackedPackIdentityTarget(identity, &SourceBackedPack{}); err == nil {
				t.Fatal("ValidateSourceBackedPackIdentityTarget error = nil, want unminted rejection")
			}
		})
	}
}

func TestFreezeSourceBackedPackIdentityDoesNotBuildFactsOrPlan(t *testing.T) {
	target := executionAtomTestPack(t)
	dependencyReads := 0
	target.components = []PackComponent{&executionAtomTestComponent{dependencyReads: &dependencyReads}}

	identity, err := FreezeSourceBackedPackIdentity(target)
	if err != nil {
		t.Fatalf("freeze identity: %v", err)
	}
	if dependencyReads != 0 {
		t.Fatalf("dependency reads = %d, want no fact or plan construction", dependencyReads)
	}
	if err := ValidateSourceBackedPackIdentityTarget(identity, target); err != nil {
		t.Fatalf("validate identity: %v", err)
	}
	if dependencyReads != 0 {
		t.Fatalf("dependency reads after validation = %d, want pure identity check", dependencyReads)
	}
}

func TestValidateSourceBackedPackIdentityTargetRequiresExactConstruction(t *testing.T) {
	target := executionAtomTestPack(t)
	identity, err := FreezeSourceBackedPackIdentity(target)
	if err != nil {
		t.Fatalf("freeze identity: %v", err)
	}
	if err := ValidateSourceBackedPackIdentityTarget(identity, target); err != nil {
		t.Fatalf("validate exact target: %v", err)
	}
	if err := ValidateSourceBackedPackIdentityTarget(identity, nil); err == nil || !strings.Contains(err.Error(), "target is nil") {
		t.Fatalf("nil target error = %v, want nil-target rejection", err)
	}

	substitute := executionAtomTestPack(t)
	if PackHashMetadataForPack(substitute) != PackHashMetadataForPack(target) {
		t.Fatal("substitute is not durable-facts-identical to target")
	}
	targetPayloadHash, err := PackPayloadHash(target)
	if err != nil {
		t.Fatalf("target payload hash: %v", err)
	}
	substitutePayloadHash, err := PackPayloadHash(substitute)
	if err != nil {
		t.Fatalf("substitute payload hash: %v", err)
	}
	targetHostDigest, err := PackHostRequirementsDigest(target)
	if err != nil {
		t.Fatalf("target host requirements: %v", err)
	}
	substituteHostDigest, err := PackHostRequirementsDigest(substitute)
	if err != nil {
		t.Fatalf("substitute host requirements: %v", err)
	}
	if targetPayloadHash != substitutePayloadHash || targetHostDigest != substituteHostDigest {
		t.Fatal("substitute is not payload-and-host-facts-identical to target")
	}
	if err := ValidateSourceBackedPackIdentityTarget(identity, substitute); err == nil || !strings.Contains(err.Error(), "different target") {
		t.Fatalf("substitute target error = %v, want exact-pointer rejection", err)
	}

	original := *target
	*target = *substitute
	if err := ValidateSourceBackedPackIdentityTarget(identity, target); err == nil || !strings.Contains(err.Error(), "different target") {
		t.Fatalf("in-place substitute error = %v, want construction-identity rejection", err)
	}
	*target = SourceBackedPack{}
	if err := ValidateSourceBackedPackIdentityTarget(identity, target); err == nil || !strings.Contains(err.Error(), "different target") {
		t.Fatalf("in-place zeroing error = %v, want construction-identity rejection", err)
	}
	*target = original
	if err := ValidateSourceBackedPackIdentityTarget(identity, target); err != nil {
		t.Fatalf("validate restored exact target: %v", err)
	}
}
