package catalogtenant

import (
	"testing"

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

const (
	testNamespace = "com.example.tenant"
	testOrg       = "org-1"
	testCommand1  = tenantauthorityv1.CommandID("01890f9e-7b2a-7000-8000-000000000001")
	testCommand2  = tenantauthorityv1.CommandID("01890f9e-7b2a-7000-8000-000000000002")
)

var testLocalKey = layoutbundlekeyv1.LocalComponentKey(
	"key256:1111111111111111111111111111111111111111111111111111111111111111",
)

func namespaceValue(
	namespace string,
	state tenantauthorityv1.NamespaceState,
) tenantauthorityv1.TenantNamespaceReservationV1 {
	return tenantauthorityv1.TenantNamespaceReservationV1{
		ValueVersion:      tenantauthorityv1.NamespaceReservationValueVersion,
		TenantNamespaceID: namespace,
		State:             state,
	}
}

func namespaceReserve(
	namespace string,
) tenantauthorityv1.TenantNamespaceCommandRequest {
	return tenantauthorityv1.TenantNamespaceCommandRequest{
		Operation:               tenantauthorityv1.NamespaceOperationReserve,
		TargetTenantNamespaceID: namespace,
		DesiredNamespaceValue: namespaceValue(
			namespace,
			tenantauthorityv1.NamespaceStateActive,
		),
		ExpectedPriorNamespace: tenantauthorityv1.TenantNamespaceExpectedPrior{
			Variant: tenantauthorityv1.NamespaceExpectedPriorAbsent,
		},
	}
}

func namespaceTombstone(
	t *testing.T,
	namespace string,
) tenantauthorityv1.TenantNamespaceCommandRequest {
	t.Helper()
	active := namespaceValue(namespace, tenantauthorityv1.NamespaceStateActive)
	return tenantauthorityv1.TenantNamespaceCommandRequest{
		Operation:               tenantauthorityv1.NamespaceOperationTombstone,
		TargetTenantNamespaceID: namespace,
		DesiredNamespaceValue: namespaceValue(
			namespace,
			tenantauthorityv1.NamespaceStateTombstone,
		),
		ExpectedPriorNamespace: tenantauthorityv1.TenantNamespaceExpectedPrior{
			Variant: tenantauthorityv1.NamespaceExpectedPriorExact,
			NamespaceValueDigest: mustNamespaceDigest(
				t,
				active,
			),
		},
	}
}

func bindingValue(
	orgID string,
	namespace string,
	state tenantauthorityv1.OrgBindingState,
) tenantauthorityv1.TenantOrgNamespaceBindingV1 {
	return tenantauthorityv1.TenantOrgNamespaceBindingV1{
		ValueVersion:      tenantauthorityv1.OrgNamespaceBindingValueVersion,
		OrgID:             orgID,
		TenantNamespaceID: namespace,
		State:             state,
	}
}

func bindingBind(
	t *testing.T,
	orgID string,
	namespace string,
) tenantauthorityv1.TenantOrgNamespaceBindingCommandRequest {
	t.Helper()
	return tenantauthorityv1.TenantOrgNamespaceBindingCommandRequest{
		Operation:           tenantauthorityv1.BindingOperationBind,
		TargetOrgID:         orgID,
		DesiredBindingValue: bindingValue(orgID, namespace, tenantauthorityv1.OrgBindingStateActive),
		ExpectedNamespaceReservationDigest: mustNamespaceDigest(
			t,
			namespaceValue(namespace, tenantauthorityv1.NamespaceStateActive),
		),
		ExpectedPriorBinding: tenantauthorityv1.TenantOrgBindingExpectedPrior{
			Variant: tenantauthorityv1.BindingExpectedPriorAbsent,
		},
	}
}

func bindingTombstone(
	t *testing.T,
	orgID string,
	namespace string,
) tenantauthorityv1.TenantOrgNamespaceBindingCommandRequest {
	t.Helper()
	active := bindingValue(orgID, namespace, tenantauthorityv1.OrgBindingStateActive)
	return tenantauthorityv1.TenantOrgNamespaceBindingCommandRequest{
		Operation:           tenantauthorityv1.BindingOperationTombstone,
		TargetOrgID:         orgID,
		DesiredBindingValue: bindingValue(orgID, namespace, tenantauthorityv1.OrgBindingStateTombstone),
		ExpectedNamespaceReservationDigest: mustNamespaceDigest(
			t,
			namespaceValue(namespace, tenantauthorityv1.NamespaceStateActive),
		),
		ExpectedPriorBinding: tenantauthorityv1.TenantOrgBindingExpectedPrior{
			Variant:            tenantauthorityv1.BindingExpectedPriorExact,
			BindingValueDigest: mustBindingDigest(t, active),
		},
	}
}

func testBundleKey(namespace string) tenantauthorityv1.BundleKey {
	return tenantauthorityv1.BundleKey{
		BundleKeyVersion:   layoutbundlekeyv1.BundleKeyVersion,
		SlotOwnerKind:      layoutbundlekeyv1.SlotOwnerKindTenant,
		SlotOwnerNamespace: namespace,
		LocalComponentKey:  testLocalKey,
	}
}

func slotValue(
	namespace string,
	state tenantauthorityv1.SlotState,
) tenantauthorityv1.TenantLayoutBundleSlotV1 {
	return tenantauthorityv1.TenantLayoutBundleSlotV1{
		ValueVersion: tenantauthorityv1.LayoutBundleSlotValueVersion,
		BundleKey:    testBundleKey(namespace),
		State:        state,
	}
}

func slotCreate(
	t *testing.T,
	orgID string,
	namespace string,
) tenantauthorityv1.TenantLayoutBundleSlotCommandRequest {
	t.Helper()
	return tenantauthorityv1.TenantLayoutBundleSlotCommandRequest{
		Operation:        tenantauthorityv1.SlotOperationCreate,
		AuthorizingOrgID: orgID,
		TargetBundleKey:  testBundleKey(namespace),
		DesiredSlotValue: slotValue(namespace, tenantauthorityv1.SlotStateActive),
		ExpectedNamespaceReservationDigest: mustNamespaceDigest(
			t,
			namespaceValue(namespace, tenantauthorityv1.NamespaceStateActive),
		),
		ExpectedAuthorizingOrgBindingDigest: mustBindingDigest(
			t,
			bindingValue(orgID, namespace, tenantauthorityv1.OrgBindingStateActive),
		),
		ExpectedPriorSlot: tenantauthorityv1.TenantSlotExpectedPrior{
			Variant: tenantauthorityv1.SlotExpectedPriorAbsent,
		},
	}
}

func slotRetire(
	t *testing.T,
	orgID string,
	namespace string,
) tenantauthorityv1.TenantLayoutBundleSlotCommandRequest {
	t.Helper()
	return tenantauthorityv1.TenantLayoutBundleSlotCommandRequest{
		Operation:        tenantauthorityv1.SlotOperationRetire,
		AuthorizingOrgID: orgID,
		TargetBundleKey:  testBundleKey(namespace),
		DesiredSlotValue: slotValue(namespace, tenantauthorityv1.SlotStateTombstone),
		ExpectedNamespaceReservationDigest: mustNamespaceDigest(
			t,
			namespaceValue(namespace, tenantauthorityv1.NamespaceStateActive),
		),
		ExpectedAuthorizingOrgBindingDigest: mustBindingDigest(
			t,
			bindingValue(orgID, namespace, tenantauthorityv1.OrgBindingStateActive),
		),
		ExpectedPriorSlot: tenantauthorityv1.TenantSlotExpectedPrior{
			Variant: tenantauthorityv1.SlotExpectedPriorExact,
			SlotValueDigest: mustSlotDigest(
				t,
				slotValue(namespace, tenantauthorityv1.SlotStateActive),
			),
		},
	}
}

func mustNamespaceDigest(
	t *testing.T,
	value tenantauthorityv1.TenantNamespaceReservationV1,
) string {
	t.Helper()
	digest, err := tenantauthorityv1.NamespaceValueDigest(value)
	if err != nil {
		t.Fatal(err)
	}
	return digest
}

func mustBindingDigest(
	t *testing.T,
	value tenantauthorityv1.TenantOrgNamespaceBindingV1,
) string {
	t.Helper()
	digest, err := tenantauthorityv1.OrgBindingValueDigest(value)
	if err != nil {
		t.Fatal(err)
	}
	return digest
}

func mustSlotDigest(
	t *testing.T,
	value tenantauthorityv1.TenantLayoutBundleSlotV1,
) string {
	t.Helper()
	digest, err := tenantauthorityv1.SlotValueDigest(value)
	if err != nil {
		t.Fatal(err)
	}
	return digest
}
