//revive:disable:package-directory-mismatch Generated protobuf package name differs from the version directory.
package nextgenv1_test

import (
	"testing"

	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
)

func TestResourcePoolInternalContractFieldNumbers(t *testing.T) {
	t.Parallel()

	assertFieldNumbers(t, "global.nextgen.v1.ResourcePool", map[protoreflect.Name]protoreflect.FieldNumber{
		"id":                      1,
		"name":                    2,
		"provider_id":             3,
		"region_id":               4,
		"owner_type":              5,
		"owner_id":                6,
		"cidr":                    7,
		"service_plan":            8,
		"instance_limit":          9,
		"k8s_service_cidr":        10,
		"config_meta_profile_id":  11,
		"dataplane_id":            12,
		"provider_identity_id":    13,
		"vcpus_limit":             14,
		"pca_arn":                 15,
		"provider_identity":       16,
		"placement_group_enabled": 17,
		"high_availability_type":  18,
		"zone_config":             19,
		"lifecycle_state":         20,
		"state_message_code":      21,
		"vcpu_limit_mode":         22,
		"cloud_resource_tags":     23,
		"created_by":              24,
		"instance_count":          25,
		"create_time":             100,
		"update_time":             101,
		"delete_time":             102,
		"paused_at":               103,
		"automatic_resume_at":     104,
	})
	assertFieldNumbers(t, "global.nextgen.v1.CreateResourcePoolRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"resource_pool":               1,
		"demand":                      2,
		"pd_deployment_mode":          3,
		"enabled_tikv_remote_workers": 4,
		"tiflash_mode":                5,
		"logical_region":              6,
		"bootstrap_capacity":          7,
		"vcpu_limit_mode":             8,
		"created_by":                  9,
		"enable_capacity_planner":     10,
		"validate_only":               100,
	})
	assertFieldNumbers(t, "global.nextgen.v1.UpdateResourcePoolInstanceLimitRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"resource_pool_id": 1,
		"instance_limit":   2,
		"vcpus_limit":      3,
		"vcpu_limit_mode":  4,
		"validate_only":    100,
	})
	assertFieldNumbers(t, "global.nextgen.v1.UpsertResourcePoolCloudResourceTagsRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"resource_pool_id":               1,
		"organization_id":                2,
		"cloud_resource_tags":            3,
		"remove_cloud_resource_tag_keys": 4,
	})
	assertFieldNumbers(t, "global.nextgen.v1.ListResourcePoolsRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"resource_pool_ids":  1,
		"resource_pool_name": 2,
		"provider_ids":       3,
		"region_ids":         4,
		"owner_type":         5,
		"owner_ids":          6,
		"created_after":      7,
		"created_before":     8,
		"service_plan":       9,
		"lifecycle_states":   10,
		"page_size":          100,
		"page":               101,
		"show_deleted":       102,
		"view":               103,
		"page_token":         104,
	})
	assertFieldNumbers(t, "global.nextgen.v1.CreateClusterRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"cluster":          1,
		"validate_only":    2,
		"resource_pool_id": 3,
	})
	assertFieldNumbers(t, "global.nextgen.v1.ListClustersRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"resource_pool_ids": 10,
	})
	assertFieldNumbers(t, "global.nextgen.v1.ListNodeSpecsRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"organization_id":   1,
		"cloud_provider_id": 2,
		"region_id":         3,
		"component_type":    4,
		"page_size":         100,
		"page_token":        101,
	})
	assertFieldNumbers(t, "global.nextgen.v1.ListNodeSpecsResponse", map[protoreflect.Name]protoreflect.FieldNumber{
		"node_specs":         1,
		"node_count_options": 2,
		"total_size":         100,
		"next_page_token":    101,
	})
	assertFieldNumbers(t, "global.nextgen.v1.PlanResourcePoolCidrRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"organization_id":   1,
		"cloud_provider_id": 2,
		"region_id":         3,
	})
	assertFieldNumbers(t, "global.nextgen.v1.ValidateResourcePoolCidrRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"organization_id":   1,
		"cloud_provider_id": 2,
		"region_id":         3,
		"cidr":              4,
	})
	assertFieldNumbers(t, "global.nextgen.v1.ValidateResourcePoolCidrResponse", map[protoreflect.Name]protoreflect.FieldNumber{
		"cloud_provider_id":    1,
		"region_id":            2,
		"cidr":                 3,
		"supported_node_count": 4,
		"blocking_findings":    5,
	})
	if !mustField(t, "global.nextgen.v1.ValidateResourcePoolCidrResponse", "supported_node_count").HasPresence() {
		t.Fatal("ValidateResourcePoolCidrResponse.supported_node_count must preserve optional presence")
	}
	assertFieldNumbers(t, "global.nextgen.v1.ResourcePoolProfile", map[protoreflect.Name]protoreflect.FieldNumber{
		"resource_pool":              1,
		"default_orgs":               12,
		"capacity":                   13,
		"provisioned_database_vcpus": 14,
		"o11y_cluster_cidr":          15,
	})
	assertFieldNumbers(t, "global.nextgen.v1.ResourcePoolPauseResumeOperation", map[protoreflect.Name]protoreflect.FieldNumber{
		"operation_id":     1,
		"resource_pool_id": 2,
		"type":             3,
		"status":           4,
		"result_summary":   5,
		"created_at":       6,
		"updated_at":       7,
		"completed_at":     8,
	})
	assertFieldNumbers(t, "global.nextgen.v1.ResourcePoolPauseValidationResult", map[protoreflect.Name]protoreflect.FieldNumber{
		"check_code": 1,
		"detail":     2,
	})
	assertFieldNumbers(t, "global.nextgen.v1.PauseResourcePoolRequest", map[protoreflect.Name]protoreflect.FieldNumber{
		"resource_pool_id": 1,
		"operation_id":     2,
		"validate_only":    100,
	})
	assertFieldNumbers(t, "global.nextgen.v1.PauseResourcePoolResponse", map[protoreflect.Name]protoreflect.FieldNumber{
		"operation_id":       1,
		"validation_results": 2,
	})

	resourcePoolID := mustField(t, "global.nextgen.v1.CreateClusterRequest", "resource_pool_id")
	if !resourcePoolID.HasPresence() {
		t.Fatal("CreateClusterRequest.resource_pool_id must preserve optional presence")
	}
}

func TestResourcePoolInternalTransportShapes(t *testing.T) {
	t.Parallel()

	assertSignedInt64(t, "global.nextgen.v1.NodeSpecSelection", "cpu")
	assertSignedInt64(t, "global.nextgen.v1.NodeSpecSelection", "memory_gi")
	assertSignedInt64(t, "global.nextgen.v1.ComponentCapacity", "node_count")
	assertSignedInt64(t, "global.nextgen.v1.Storage", "size_gi")
	assertSignedInt64(t, "global.nextgen.v1.ResourcePoolNodeSpec", "cpu")
	assertSignedInt64(t, "global.nextgen.v1.ResourcePoolNodeSpec", "memory_gi")
	assertSignedInt64(t, "global.nextgen.v1.NodeSpecStorage", "default_size_gi")
	assertSignedInt64(t, "global.nextgen.v1.NodeSpecStorage", "min_size_gi")
	assertSignedInt64(t, "global.nextgen.v1.NodeSpecStorage", "max_size_gi")
	assertSignedInt64(t, "global.nextgen.v1.NodeCountOptions", "default_node_count")
	assertSignedInt64(t, "global.nextgen.v1.NodeCountOptions", "min_node_count")
	assertSignedInt64(t, "global.nextgen.v1.NodeCountOptions", "max_node_count")
	assertSignedInt64(t, "global.nextgen.v1.NodeCountRule", "value")
	assertSignedInt64(t, "global.nextgen.v1.NodeCountConstraints", "current")
	assertSignedInt64(t, "global.nextgen.v1.NodeCountConstraints", "min")
	assertSignedInt64(t, "global.nextgen.v1.NodeCountConstraints", "max")
	assertSignedInt64(t, "global.nextgen.v1.ResourcePool", "instance_count")

	for _, field := range []struct {
		message protoreflect.FullName
		field   protoreflect.Name
	}{
		{"global.nextgen.v1.ComponentCapacity", "node_count"},
		{"global.nextgen.v1.NodeSpecStorage", "resizable"},
		{"global.nextgen.v1.NodeSpecStorage", "current_size_gi"},
		{"global.nextgen.v1.ResourcePoolProfile", "provisioned_database_vcpus"},
		{"global.nextgen.v1.ResourcePoolProfile", "o11y_cluster_cidr"},
	} {
		if !mustField(t, field.message, field.field).HasPresence() {
			t.Fatalf("%s.%s must preserve optional presence", field.message, field.field)
		}
	}

	cloudResourceTags := mustField(t, "global.nextgen.v1.UpsertResourcePoolCloudResourceTagsRequest", "cloud_resource_tags")
	if !cloudResourceTags.IsMap() {
		t.Fatal("UpsertResourcePoolCloudResourceTagsRequest.cloud_resource_tags must be a map")
	}
	removeCloudResourceTagKeys := mustField(t, "global.nextgen.v1.UpsertResourcePoolCloudResourceTagsRequest", "remove_cloud_resource_tag_keys")
	if !removeCloudResourceTagKeys.IsList() || removeCloudResourceTagKeys.Kind() != protoreflect.StringKind {
		t.Fatal("UpsertResourcePoolCloudResourceTagsRequest.remove_cloud_resource_tag_keys must be a repeated string")
	}

	assertEnumValues(t, "global.nextgen.v1.ResourcePool.LifecycleState", map[protoreflect.Name]protoreflect.EnumNumber{
		"LIFECYCLE_STATE_UNSPECIFIED": 0,
		"CREATING":                    1,
		"ACTIVE":                      2,
		"MODIFYING":                   3,
		"FAILED":                      4,
		"PAUSING":                     5,
		"PAUSED":                      6,
		"RESUMING":                    7,
	})
	assertEnumValues(t, "global.nextgen.v1.ResourcePool.VcpuLimitMode", map[protoreflect.Name]protoreflect.EnumNumber{
		"VCPU_LIMIT_MODE_UNSPECIFIED": 0,
		"INHERITED":                   1,
		"LIMITED":                     2,
		"UNLIMITED":                   3,
	})
}

func TestResourcePoolInternalServiceMethods(t *testing.T) {
	t.Parallel()

	assertMethods(t, "global.nextgen.v1.ResourcePoolService", map[protoreflect.Name][2]protoreflect.FullName{
		"GetResourcePoolCapacityConstraints": {
			"global.nextgen.v1.GetResourcePoolCapacityConstraintsRequest",
			"global.nextgen.v1.ResourcePoolCapacityConstraints",
		},
		"ResizeResourcePoolCapacity": {
			"global.nextgen.v1.ResizeResourcePoolCapacityRequest",
			"global.nextgen.v1.ResizeResourcePoolCapacityResponse",
		},
		"UpsertResourcePoolCloudResourceTags": {
			"global.nextgen.v1.UpsertResourcePoolCloudResourceTagsRequest",
			"global.nextgen.v1.UpsertResourcePoolCloudResourceTagsResponse",
		},
		"PauseResourcePool": {
			"global.nextgen.v1.PauseResourcePoolRequest",
			"global.nextgen.v1.PauseResourcePoolResponse",
		},
		"ResumeResourcePool": {
			"global.nextgen.v1.ResumeResourcePoolRequest",
			"global.nextgen.v1.ResumeResourcePoolResponse",
		},
		"GetResourcePoolPauseResumeOperation": {
			"global.nextgen.v1.GetResourcePoolPauseResumeOperationRequest",
			"global.nextgen.v1.ResourcePoolPauseResumeOperation",
		},
	})
	assertMethods(t, "global.nextgen.v1.RegionService", map[protoreflect.Name][2]protoreflect.FullName{
		"ListNodeSpecs":            {"global.nextgen.v1.ListNodeSpecsRequest", "global.nextgen.v1.ListNodeSpecsResponse"},
		"PlanResourcePoolCidr":     {"global.nextgen.v1.PlanResourcePoolCidrRequest", "global.nextgen.v1.PlanResourcePoolCidrResponse"},
		"ValidateResourcePoolCidr": {"global.nextgen.v1.ValidateResourcePoolCidrRequest", "global.nextgen.v1.ValidateResourcePoolCidrResponse"},
	})
}

func TestResourcePoolInternalLegacyWireCompatibility(t *testing.T) {
	t.Parallel()

	createResourcePool := &globalngv1.CreateResourcePoolRequest{}
	if err := proto.Unmarshal([]byte{0x0a, 0x00, 0x12, 0x00, 0xa0, 0x06, 0x01}, createResourcePool); err != nil {
		t.Fatalf("unmarshal legacy CreateResourcePoolRequest: %v", err)
	}
	if createResourcePool.GetResourcePool() == nil || createResourcePool.GetDemand() == nil || !createResourcePool.GetValidateOnly() {
		t.Fatalf("legacy CreateResourcePoolRequest fields did not decode: %v", createResourcePool)
	}

	createCluster := &globalngv1.CreateClusterRequest{}
	if err := proto.Unmarshal([]byte{0x0a, 0x00, 0x10, 0x01}, createCluster); err != nil {
		t.Fatalf("unmarshal legacy CreateClusterRequest: %v", err)
	}
	if createCluster.GetCluster() == nil || !createCluster.GetValidateOnly() || createCluster.ResourcePoolId != nil {
		t.Fatalf("legacy CreateClusterRequest fields did not decode without selecting a resource pool: %v", createCluster)
	}
}

func assertFieldNumbers(t *testing.T, messageName protoreflect.FullName, want map[protoreflect.Name]protoreflect.FieldNumber) {
	t.Helper()

	message := mustMessage(t, messageName)
	for fieldName, number := range want {
		field := message.Fields().ByName(fieldName)
		if field == nil {
			t.Errorf("field %s.%s not found", messageName, fieldName)
			continue
		}
		if field.Number() != number {
			t.Errorf("field %s.%s number = %d, want %d", messageName, fieldName, field.Number(), number)
		}
	}
}

func assertSignedInt64(t *testing.T, messageName protoreflect.FullName, fieldName protoreflect.Name) {
	t.Helper()

	field := mustField(t, messageName, fieldName)
	if field.Kind() != protoreflect.Int64Kind {
		t.Errorf("field %s.%s kind = %s, want int64", messageName, fieldName, field.Kind())
	}
}

func assertMethods(t *testing.T, serviceName protoreflect.FullName, want map[protoreflect.Name][2]protoreflect.FullName) {
	t.Helper()

	service := mustService(t, serviceName)
	for methodName, types := range want {
		method := service.Methods().ByName(methodName)
		if method == nil {
			t.Errorf("method %s.%s not found", serviceName, methodName)
			continue
		}
		if method.Input().FullName() != types[0] || method.Output().FullName() != types[1] {
			t.Errorf(
				"method %s.%s = (%s) returns (%s), want (%s) returns (%s)",
				serviceName,
				methodName,
				method.Input().FullName(),
				method.Output().FullName(),
				types[0],
				types[1],
			)
		}
	}
}

func assertEnumValues(t *testing.T, enumName protoreflect.FullName, want map[protoreflect.Name]protoreflect.EnumNumber) {
	t.Helper()

	descriptor, err := protoregistry.GlobalFiles.FindDescriptorByName(enumName)
	if err != nil {
		t.Fatalf("enum %s not found: %v", enumName, err)
	}
	enum, ok := descriptor.(protoreflect.EnumDescriptor)
	if !ok {
		t.Fatalf("descriptor %s has unexpected type %T", enumName, descriptor)
	}
	for valueName, number := range want {
		value := enum.Values().ByName(valueName)
		if value == nil {
			t.Errorf("enum value %s.%s not found", enumName, valueName)
			continue
		}
		if value.Number() != number {
			t.Errorf("enum value %s.%s number = %d, want %d", enumName, valueName, value.Number(), number)
		}
	}
}

func mustField(t *testing.T, messageName protoreflect.FullName, fieldName protoreflect.Name) protoreflect.FieldDescriptor {
	t.Helper()

	field := mustMessage(t, messageName).Fields().ByName(fieldName)
	if field == nil {
		t.Fatalf("field %s.%s not found", messageName, fieldName)
	}
	return field
}

func mustMessage(t *testing.T, name protoreflect.FullName) protoreflect.MessageDescriptor {
	t.Helper()

	descriptor, err := protoregistry.GlobalFiles.FindDescriptorByName(name)
	if err != nil {
		t.Fatalf("message %s not found: %v", name, err)
	}
	message, ok := descriptor.(protoreflect.MessageDescriptor)
	if !ok {
		t.Fatalf("descriptor %s has unexpected type %T", name, descriptor)
	}
	return message
}

func mustService(t *testing.T, name protoreflect.FullName) protoreflect.ServiceDescriptor {
	t.Helper()

	descriptor, err := protoregistry.GlobalFiles.FindDescriptorByName(name)
	if err != nil {
		t.Fatalf("service %s not found: %v", name, err)
	}
	service, ok := descriptor.(protoreflect.ServiceDescriptor)
	if !ok {
		t.Fatalf("descriptor %s has unexpected type %T", name, descriptor)
	}
	return service
}
