package catalogassignmentidentityv1

import (
	"reflect"
	"testing"

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

func TestViewHasOnlyTheFrozenValueSurface(t *testing.T) {
	want := []struct {
		name   string
		typeOf reflect.Type
	}{
		{"SourceCatalogSnapshot", reflect.TypeOf(catalogpublicationv1.CatalogSnapshotBinding{})},
		{"CanonicalizationVersion", reflect.TypeOf("")},
		{"KeyGrammarVersion", reflect.TypeOf("")},
		{"ObjectKeyNormalizationVersion", reflect.TypeOf("")},
		{"Objects", reflect.TypeOf([]ObjectIdentity{})},
		{"RecordTypes", reflect.TypeOf([]RecordTypeIdentity{})},
		{"ProductApps", reflect.TypeOf([]ProductAppIdentity{})},
		{"NavigationApps", reflect.TypeOf([]NavigationAppIdentity{})},
	}

	viewType := reflect.TypeOf(View{})
	if viewType.NumField() != len(want) {
		t.Fatalf("View has %d fields, want exactly %d", viewType.NumField(), len(want))
	}
	for index, expected := range want {
		field := viewType.Field(index)
		if field.Name != expected.name || field.Type != expected.typeOf {
			t.Fatalf("View field[%d] = %s %s, want %s %s", index, field.Name, field.Type, expected.name, expected.typeOf)
		}
	}
}

func TestStableKeyTypesRemainDistinct(t *testing.T) {
	types := []reflect.Type{
		reflect.TypeOf(ObjectKey{}),
		reflect.TypeOf(RecordTypeKey{}),
		reflect.TypeOf(ProductAppKey{}),
		reflect.TypeOf(NavigationAppKey{}),
	}
	for left := range types {
		for right := left + 1; right < len(types); right++ {
			if types[left] == types[right] || types[left].AssignableTo(types[right]) {
				t.Fatalf("stable key types %s and %s must not be interchangeable", types[left], types[right])
			}
		}
	}
}

func TestProductAppResolvedTargetSurfaceIsClosed(t *testing.T) {
	if ProductAppResolvedTargetKindAbsent != 0x00 {
		t.Fatalf("ABSENT tag = %#x, want 0x00", ProductAppResolvedTargetKindAbsent)
	}
	if ProductAppResolvedTargetKindAppInstallationID != 0x01 {
		t.Fatalf("APP_INSTALLATION_ID tag = %#x, want 0x01", ProductAppResolvedTargetKindAppInstallationID)
	}

	targetType := reflect.TypeOf(ProductAppResolvedTarget{})
	targetFields := []struct {
		name   string
		typeOf reflect.Type
	}{
		{"Kind", reflect.TypeOf(ProductAppResolvedTargetKind(0))},
		{"ResolvedAppInstallationID", reflect.TypeOf("")},
	}
	if targetType.NumField() != len(targetFields) {
		t.Fatalf("ProductAppResolvedTarget has %d fields, want exactly %d", targetType.NumField(), len(targetFields))
	}
	for index, expected := range targetFields {
		field := targetType.Field(index)
		if field.Name != expected.name || field.Type != expected.typeOf {
			t.Fatalf("ProductAppResolvedTarget field[%d] = %s %s, want %s %s", index, field.Name, field.Type, expected.name, expected.typeOf)
		}
	}

	identityType := reflect.TypeOf(ProductAppIdentity{})
	identityFields := []struct {
		name   string
		typeOf reflect.Type
	}{
		{"Key", reflect.TypeOf(ProductAppKey{})},
		{"State", reflect.TypeOf(IdentityState(""))},
		{"ResolvedTarget", reflect.TypeOf(ProductAppResolvedTarget{})},
	}
	if identityType.NumField() != len(identityFields) {
		t.Fatalf("ProductAppIdentity has %d fields, want exactly %d", identityType.NumField(), len(identityFields))
	}
	for index, expected := range identityFields {
		field := identityType.Field(index)
		if field.Name != expected.name || field.Type != expected.typeOf {
			t.Fatalf("ProductAppIdentity field[%d] = %s %s, want %s %s", index, field.Name, field.Type, expected.name, expected.typeOf)
		}
	}
}
