package catalogpublication

import (
	"strings"
	"testing"

	catalogpublicationv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalogpublicationv1"
)

func TestPublicationCommandDigestBindsSemanticRequestButNotCommandIdentity(t *testing.T) {
	request := validBeginRequest()
	candidateDigest, digest, err := validateBeginRequest(request)
	if err != nil {
		t.Fatal(err)
	}
	if candidateDigest == "" || digest == "" {
		t.Fatal("valid request did not acquire both digests")
	}

	otherCommand := request
	otherCommand.CommandID = "018f0000-0000-7000-9000-000000000002"
	_, otherDigest, err := validateBeginRequest(otherCommand)
	if err != nil || otherDigest != digest {
		t.Fatalf("command identity changed semantic request digest: %s, %v", otherDigest, err)
	}

	mutations := map[string]func(*BeginRequest){
		"RuntimeAudience": func(value *BeginRequest) { value.RuntimeAudience = "runtime-next" },
		"ExpectedPriorPresence": func(value *BeginRequest) {
			value.ExpectedPrior = Reference{Present: true, Digest: testDigest("9")}
		},
		"Candidate": func(value *BeginRequest) { value.Candidate.CatalogRevision++ },
	}
	for name, mutate := range mutations {
		t.Run(name, func(t *testing.T) {
			changed := request
			mutate(&changed)
			_, changedDigest, err := validateBeginRequest(changed)
			if err != nil {
				t.Fatal(err)
			}
			if changedDigest == digest {
				t.Fatal("semantic change did not change request digest")
			}
		})
	}
}

func TestBeginRequestValidationFailsClosed(t *testing.T) {
	tests := map[string]func(*BeginRequest){
		"non-v7 command":               func(value *BeginRequest) { value.CommandID = "018f0000-0000-4000-9000-000000000001" },
		"uppercase command":            func(value *BeginRequest) { value.CommandID = "018F0000-0000-7000-9000-000000000001" },
		"blank audience":               func(value *BeginRequest) { value.RuntimeAudience = "" },
		"oversized audience":           func(value *BeginRequest) { value.RuntimeAudience = strings.Repeat("a", 256) },
		"absent prior with digest":     func(value *BeginRequest) { value.ExpectedPrior.Digest = testDigest("9") },
		"present prior without digest": func(value *BeginRequest) { value.ExpectedPrior = Reference{Present: true} },
		"invalid candidate":            func(value *BeginRequest) { value.Candidate.SecurityEpoch = 0 },
	}
	for name, mutate := range tests {
		t.Run(name, func(t *testing.T) {
			request := validBeginRequest()
			mutate(&request)
			if _, _, err := validateBeginRequest(request); err == nil {
				t.Fatal("invalid request accepted")
			}
		})
	}
}

func validBeginRequest() BeginRequest {
	return BeginRequest{
		CommandID:       "018f0000-0000-7000-9000-000000000001",
		RuntimeAudience: "runtime",
		ExpectedPrior:   Reference{},
		Candidate: catalogpublicationv1.Binding{
			OrgID:                           "org-1",
			CatalogRevision:                 1,
			CatalogSnapshotDigest:           testDigest("1"),
			SchemaGeneration:                1,
			EngineGeneration:                1,
			LayoutValidationViewDigest:      testDigest("2"),
			LayoutActivationSetDigest:       testDigest("3"),
			SecurityEpoch:                   1,
			LayoutRoutingSecurityViewDigest: testDigest("4"),
		},
	}
}

func testDigest(digit string) string {
	return "sha256:" + strings.Repeat(digit, 64)
}
