package catalogtenant

import (
	"testing"

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

func TestTerminalOutcomeDigestCommitsEveryOutcomeField(t *testing.T) {
	request := slotCreate(t, testOrg, testNamespace)
	requestDigest, err := tenantauthorityv1.SlotCommandRequestDigest(request)
	if err != nil {
		t.Fatal(err)
	}
	desiredDigest := mustSlotDigest(t, request.DesiredSlotValue)
	base, err := completeTerminalCommand(terminalCommand{
		CommandID:     testCommand1,
		Kind:          commandKindSlot,
		RequestDigest: requestDigest,
		Result:        ResultApplied,
		ResultingValue: ValueReference{
			Present: true,
			Digest:  desiredDigest,
		},
		ObservedNamespace: ValueReference{
			Present: true,
			Digest:  request.ExpectedNamespaceReservationDigest,
		},
		ObservedBinding: ValueReference{
			Present: true,
			Digest:  request.ExpectedAuthorizingOrgBindingDigest,
		},
		Sequence: 7,
	})
	if err != nil {
		t.Fatal(err)
	}
	if err := validateSlotOutcome(request, base); err != nil {
		t.Fatal(err)
	}
	if err := validateDurableTerminalCommand(base); err != nil {
		t.Fatal(err)
	}

	mutations := map[string]func(*terminalCommand){
		"command": func(value *terminalCommand) {
			value.CommandID = testCommand2
		},
		"kind": func(value *terminalCommand) {
			value.Kind = commandKindBinding
		},
		"request": func(value *terminalCommand) {
			value.RequestDigest = desiredDigest
		},
		"result": func(value *terminalCommand) {
			value.Result = ResultExpectedPriorMismatch
			value.ResultingValue = value.ObservedPrior
		},
		"prior": func(value *terminalCommand) {
			value.ObservedPrior = ValueReference{Present: true, Digest: desiredDigest}
		},
		"resulting": func(value *terminalCommand) {
			value.ResultingValue = ValueReference{}
		},
		"namespace": func(value *terminalCommand) {
			value.ObservedNamespace = ValueReference{}
		},
		"binding": func(value *terminalCommand) {
			value.ObservedBinding = ValueReference{}
		},
		"sequence": func(value *terminalCommand) {
			value.Sequence++
		},
	}
	for name, mutate := range mutations {
		t.Run(name, func(t *testing.T) {
			changed := base
			mutate(&changed)
			changed.OutcomeDigest = ""
			digest, err := terminalOutcomeDigest(changed)
			if err != nil {
				return
			}
			if digest == base.OutcomeDigest {
				t.Fatal("mutation retained the outcome digest")
			}
		})
	}
}

func TestDurableOutcomeRejectsStoredDigestDrift(t *testing.T) {
	request := namespaceReserve(testNamespace)
	requestDigest, err := tenantauthorityv1.NamespaceCommandRequestDigest(request)
	if err != nil {
		t.Fatal(err)
	}
	command, err := completeTerminalCommand(terminalCommand{
		CommandID:     testCommand1,
		Kind:          commandKindNamespace,
		RequestDigest: requestDigest,
		Result:        ResultApplied,
		ResultingValue: ValueReference{
			Present: true,
			Digest:  mustNamespaceDigest(t, request.DesiredNamespaceValue),
		},
		Sequence: 1,
	})
	if err != nil {
		t.Fatal(err)
	}
	command.OutcomeDigest = "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
	if err := validateDurableTerminalCommand(command); err == nil {
		t.Fatal("changed stored outcome digest was accepted")
	}
}
