package ngoperation

import (
	"context"
	"strings"
	"testing"

	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/infra/clients/coresvccli"
	commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
)

func buildCreateResourcePoolInstance(params map[opent.ParamName]string) *opent.Instance {
	return &opent.Instance{
		Params: params,
	}
}

func baseCreateResourcePoolParams() map[opent.ParamName]string {
	return map[opent.ParamName]string{
		opent.ParamProvider:                            "AWS",
		opent.ParamRegionName:                          "us-east-1",
		opent.ParamCIDR:                                "10.0.0.0/16",
		opent.ParamServicePlan:                         commonv1.ServicePlan_Premium.String(),
		opent.ParamName("name"):                        "rp-test",
		opent.ParamK8SServiceCIDR:                      "10.1.0.0/16",
		opent.ParamTenantID:                            "1",
		opent.ParamInitialRcu:                          "10",
		opent.ParamInstanceLimit:                       "2",
		opent.ParamVcpusLimit:                          "4",
		opent.ParamMetaProfileID:                       "meta-1",
		opent.ParamPCAArn:                              "arn:aws:iam::123456789012:role/pca",
		opent.ParamName("tikv_data.volume_type"):       "GP3",
		opent.ParamName("tikv_raftlog.volume_type"):    "GP3",
		opent.ParamName("tiflash_compute.volume_type"): "GP3",
		opent.ParamName("tiflash_write.volume_type"):   "GP3",
	}
}

func Test_createResourcePool_getParam_withProviderIdentity(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamProviderIdentity] = "aws-account-123"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetResourcePool().GetProviderIdentity() != "aws-account-123" {
		t.Fatalf("provider identity = %q, want %q", got.GetResourcePool().GetProviderIdentity(), "aws-account-123")
	}
}

func Test_createResourcePool_getParam_withoutProviderIdentity(t *testing.T) {
	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(baseCreateResourcePoolParams()))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetResourcePool().GetProviderIdentity() != "" {
		t.Fatalf("provider identity = %q, want empty", got.GetResourcePool().GetProviderIdentity())
	}
}

func Test_createResourcePool_getParam_withCapacityPlannerEnabled(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamEnableCapacityPlanner] = "true"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if !got.GetEnableCapacityPlanner() {
		t.Fatal("capacity planner enabled = false, want true")
	}
}

func Test_createResourcePool_getParam_withInvalidCapacityPlannerEnabled(t *testing.T) {
	const invalidValue = "not-a-bool"

	params := baseCreateResourcePoolParams()
	params[opent.ParamEnableCapacityPlanner] = invalidValue

	_, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(params))
	if err == nil ||
		!strings.Contains(err.Error(), string(opent.ParamEnableCapacityPlanner)) ||
		!strings.Contains(err.Error(), invalidValue) {
		t.Fatalf("getParam() error = %v, want invalid capacity planner error", err)
	}
}

func Test_createResourcePool_getParam_withPlacementGroupEnabled(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamPlacementGroupEnabled] = "true"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if !got.GetResourcePool().GetPlacementGroupEnabled() {
		t.Fatal("placement group enabled = false, want true")
	}
}

func Test_createResourcePool_getParam_withoutPlacementGroupEnabled(t *testing.T) {
	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(baseCreateResourcePoolParams()))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetResourcePool().GetPlacementGroupEnabled() {
		t.Fatal("placement group enabled = true, want false")
	}
}

func Test_createResourcePool_getParam_withPdDeploymentModeMicroservice(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamPdDeploymentMode] = "MICROSERVICE"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetPdDeploymentMode() != nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_MICROSERVICE {
		t.Fatalf("pd deployment mode = %s, want %s", got.GetPdDeploymentMode(), nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_MICROSERVICE)
	}
}

func Test_createResourcePool_getParam_withPdDeploymentModeMonolithic(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamPdDeploymentMode] = "MONOLITHIC"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetPdDeploymentMode() != nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_MONOLITHIC {
		t.Fatalf("pd deployment mode = %s, want %s", got.GetPdDeploymentMode(), nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_MONOLITHIC)
	}
}

func Test_createResourcePool_getParam_withoutPdDeploymentMode(t *testing.T) {
	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(baseCreateResourcePoolParams()))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetPdDeploymentMode() != nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED {
		t.Fatalf("pd deployment mode = %s, want %s", got.GetPdDeploymentMode(), nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED)
	}
}

func Test_createResourcePool_getParam_withEmptyPdDeploymentMode(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamPdDeploymentMode] = ""

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetPdDeploymentMode() != nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED {
		t.Fatalf("pd deployment mode = %s, want %s", got.GetPdDeploymentMode(), nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED)
	}
}

func Test_createResourcePool_getParam_withEnabledTiKVRemoteWorkersTiKVOnly(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamEnabledTiKVRemoteWorkers] = "TIKV_WORKER"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	expectEnabledTiKVRemoteWorkers(t, got.GetEnabledTikvRemoteWorkers(), []nextgenv1.ComponentType{
		nextgenv1.ComponentType_TIKV_WORKER,
	})
}

func Test_createResourcePool_getParam_withEnabledTiKVRemoteWorkersTiKVAndCoprocessor(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamEnabledTiKVRemoteWorkers] = "TIKV_WORKER,COPROCESSOR_WORKER"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	expectEnabledTiKVRemoteWorkers(t, got.GetEnabledTikvRemoteWorkers(), []nextgenv1.ComponentType{
		nextgenv1.ComponentType_TIKV_WORKER,
		nextgenv1.ComponentType_COPROCESSOR_WORKER,
	})
}

func Test_createResourcePool_getParam_withoutEnabledTiKVRemoteWorkers(t *testing.T) {
	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(baseCreateResourcePoolParams()))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if len(got.GetEnabledTikvRemoteWorkers()) != 0 {
		t.Fatalf("enabled tikv remote workers = %v, want empty", got.GetEnabledTikvRemoteWorkers())
	}
}

func Test_createResourcePool_getParam_withEmptyEnabledTiKVRemoteWorkers(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamEnabledTiKVRemoteWorkers] = ""

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if len(got.GetEnabledTikvRemoteWorkers()) != 0 {
		t.Fatalf("enabled tikv remote workers = %v, want empty", got.GetEnabledTikvRemoteWorkers())
	}
}

func Test_createResourcePool_getParam_withTiflashMode(t *testing.T) {
	tests := []struct {
		value string
		want  nextgenv1.TiflashMode
	}{
		{value: "AUTO", want: nextgenv1.TiflashMode_TIFLASH_MODE_AUTO},
		{value: "LEGACY", want: nextgenv1.TiflashMode_TIFLASH_MODE_LEGACY},
		{value: "COLUMNAR", want: nextgenv1.TiflashMode_TIFLASH_MODE_COLUMNAR},
	}

	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			params := baseCreateResourcePoolParams()
			params[opent.ParamTiflashMode] = tt.value

			got, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(params))
			if err != nil {
				t.Fatalf("getParam() error = %v", err)
			}
			if got.GetTiflashMode() != tt.want {
				t.Fatalf("tiflash mode = %s, want %s", got.GetTiflashMode(), tt.want)
			}
		})
	}
}

func Test_createResourcePool_getParam_withoutTiflashMode(t *testing.T) {
	got, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(baseCreateResourcePoolParams()))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}
	if got.GetTiflashMode() != nextgenv1.TiflashMode_TIFLASH_MODE_UNSPECIFIED {
		t.Fatalf("tiflash mode = %s, want %s", got.GetTiflashMode(), nextgenv1.TiflashMode_TIFLASH_MODE_UNSPECIFIED)
	}
}

func Test_createResourcePool_getParam_rejectsInvalidTiflashMode(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamTiflashMode] = "TIFLASH_MODE_COLUMNAR"

	_, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(params))
	if err == nil {
		t.Fatal("getParam() expected error, got nil")
	}
	if !strings.Contains(err.Error(), string(opent.ParamTiflashMode)) {
		t.Fatalf("getParam() error = %q, want contains %q", err.Error(), opent.ParamTiflashMode)
	}
}

func Test_createResourcePool_getParam_rejectsInvalidPdDeploymentMode(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamPdDeploymentMode] = "INVALID"

	a := &createResourcePool{}
	_, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err == nil {
		t.Fatal("getParam() expected error, got nil")
	}
	if !strings.Contains(err.Error(), string(opent.ParamPdDeploymentMode)) {
		t.Fatalf("getParam() error = %q, want contains %q", err.Error(), opent.ParamPdDeploymentMode)
	}
}

func Test_createResourcePool_getParam_rejectsProtoNamePdDeploymentMode(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamPdDeploymentMode] = nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_MICROSERVICE.String()

	a := &createResourcePool{}
	_, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err == nil {
		t.Fatal("getParam() expected error, got nil")
	}
	if !strings.Contains(err.Error(), string(opent.ParamPdDeploymentMode)) {
		t.Fatalf("getParam() error = %q, want contains %q", err.Error(), opent.ParamPdDeploymentMode)
	}
}

func Test_createResourcePool_getParam_rejectsExplicitUnspecifiedPdDeploymentMode(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamPdDeploymentMode] = nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED.String()

	a := &createResourcePool{}
	_, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err == nil {
		t.Fatal("getParam() expected error, got nil")
	}
	if !strings.Contains(err.Error(), string(opent.ParamPdDeploymentMode)) {
		t.Fatalf("getParam() error = %q, want contains %q", err.Error(), opent.ParamPdDeploymentMode)
	}
}

func Test_createResourcePool_getParam_rejectsInvalidEnabledTiKVRemoteWorkers(t *testing.T) {
	tests := []struct {
		name  string
		value string
	}{
		{name: "invalid value", value: "INVALID"},
		{name: "reversed workers", value: "COPROCESSOR_WORKER,TIKV_WORKER"},
		{name: "space after comma", value: "TIKV_WORKER, COPROCESSOR_WORKER"},
		{name: "json list", value: `["TIKV_WORKER","COPROCESSOR_WORKER"]`},
		{name: "unsupported component", value: "TIDB_WORKER"},
		{name: "coprocessor only", value: "COPROCESSOR_WORKER"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			params := baseCreateResourcePoolParams()
			params[opent.ParamEnabledTiKVRemoteWorkers] = tt.value

			a := &createResourcePool{}
			_, err := a.getParam(buildCreateResourcePoolInstance(params))
			if err == nil {
				t.Fatal("getParam() expected error, got nil")
			}
			if !strings.Contains(err.Error(), string(opent.ParamEnabledTiKVRemoteWorkers)) {
				t.Fatalf("getParam() error = %q, want contains %q", err.Error(), opent.ParamEnabledTiKVRemoteWorkers)
			}
		})
	}
}

func Test_createResourcePool_getParam_rejectsInvalidPlacementGroupEnabled(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamPlacementGroupEnabled] = "invalid"

	a := &createResourcePool{}
	_, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err == nil {
		t.Fatal("getParam() expected error, got nil")
	}
	if !strings.Contains(err.Error(), string(opent.ParamPlacementGroupEnabled)) {
		t.Fatalf("getParam() error = %q, want contains %q", err.Error(), opent.ParamPlacementGroupEnabled)
	}
}

func expectEnabledTiKVRemoteWorkers(t *testing.T, got []nextgenv1.ComponentType, want []nextgenv1.ComponentType) {
	t.Helper()

	if len(got) != len(want) {
		t.Fatalf("enabled tikv remote workers = %v, want %v", got, want)
	}
	for i := range want {
		if got[i] != want[i] {
			t.Fatalf("enabled tikv remote workers = %v, want %v", got, want)
		}
	}
}

func Test_createResourcePool_getParam_missingRequiredParam(t *testing.T) {
	params := baseCreateResourcePoolParams()
	delete(params, opent.ParamProvider)

	a := &createResourcePool{}
	_, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err == nil {
		t.Fatal("getParam() expected error, got nil")
	}
	if !strings.Contains(err.Error(), string(opent.ParamProvider)) {
		t.Fatalf("getParam() error = %q, want contains %q", err.Error(), opent.ParamProvider)
	}
}

func Test_createResourcePool_getParam_rejectsUnsupportedServicePlan(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamServicePlan] = commonv1.ServicePlan_Premium_Reserved.String()

	a := &createResourcePool{}
	_, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err == nil {
		t.Fatal("getParam() expected error, got nil")
	}
	if !strings.Contains(err.Error(), "not supported") {
		t.Fatalf("getParam() error = %q, want contains not supported", err.Error())
	}
}

func Test_createResourcePool_getParam_supportsSharedOwnerByTenantZero(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamTenantID] = "0"
	params[opent.ParamServicePlan] = commonv1.ServicePlan_Premium_Reserved.String()

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetResourcePool().GetOwnerType() != nextgenv1.ResourcePool_SHARED {
		t.Fatalf("owner type = %s, want %s", got.GetResourcePool().GetOwnerType().String(), nextgenv1.ResourcePool_SHARED.String())
	}
	if got.GetResourcePool().GetOwnerId() != 0 {
		t.Fatalf("owner id = %d, want 0", got.GetResourcePool().GetOwnerId())
	}
}

func Test_createResourcePool_getParam_supportsEnabledTiKVRemoteWorkersForSharedOwner(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamTenantID] = "0"
	params[opent.ParamServicePlan] = commonv1.ServicePlan_Premium_Reserved.String()
	params[opent.ParamEnabledTiKVRemoteWorkers] = "TIKV_WORKER,COPROCESSOR_WORKER"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetResourcePool().GetOwnerType() != nextgenv1.ResourcePool_SHARED {
		t.Fatalf("owner type = %s, want %s", got.GetResourcePool().GetOwnerType().String(), nextgenv1.ResourcePool_SHARED.String())
	}
	expectEnabledTiKVRemoteWorkers(t, got.GetEnabledTikvRemoteWorkers(), []nextgenv1.ComponentType{
		nextgenv1.ComponentType_TIKV_WORKER,
		nextgenv1.ComponentType_COPROCESSOR_WORKER,
	})
}

func Test_createResourcePool_getParam_withLogicalRegionForEssentialV2(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamTenantID] = "0"
	params[opent.ParamServicePlan] = commonv1.ServicePlan_Essential_V2.String()
	params[opent.ParamLogicalRegion] = "us-west-2-a0"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetLogicalRegion() != "us-west-2-a0" {
		t.Fatalf("logical region = %q, want %q", got.GetLogicalRegion(), "us-west-2-a0")
	}
}

func Test_createResourcePool_getParam_withoutLogicalRegionForEssentialV2(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamTenantID] = "0"
	params[opent.ParamServicePlan] = commonv1.ServicePlan_Essential_V2.String()

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetLogicalRegion() != "" {
		t.Fatalf("logical region = %q, want empty", got.GetLogicalRegion())
	}
}

func Test_createResourcePool_getParam_ignoresLogicalRegionForNonEssentialV2(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamTenantID] = "0"
	params[opent.ParamServicePlan] = commonv1.ServicePlan_Premium_Reserved.String()
	params[opent.ParamLogicalRegion] = "us-west-2-a0"

	a := &createResourcePool{}
	got, err := a.getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetLogicalRegion() != "" {
		t.Fatalf("logical region = %q, want empty (ignored for non-Essential_V2)", got.GetLogicalRegion())
	}
}

func Test_isOriginalResourcePoolServicePlan(t *testing.T) {
	cases := []struct {
		plan commonv1.ServicePlan
		want bool
	}{
		{plan: commonv1.ServicePlan_Premium, want: true},
		{plan: commonv1.ServicePlan_BYOC, want: true},
		{plan: commonv1.ServicePlan_Premium_Reserved, want: false},
		{plan: commonv1.ServicePlan_Essential_V2, want: false},
	}

	for _, tc := range cases {
		if got := isOriginalResourcePoolServicePlan(tc.plan); got != tc.want {
			t.Fatalf("isOriginalResourcePoolServicePlan(%s) = %v, want %v", tc.plan.String(), got, tc.want)
		}
	}
}

func Test_isSetDefaultResourcePoolServicePlan(t *testing.T) {
	cases := []struct {
		plan commonv1.ServicePlan
		want bool
	}{
		{plan: commonv1.ServicePlan_Premium, want: true},
		{plan: commonv1.ServicePlan_BYOC, want: true},
		{plan: commonv1.ServicePlan_Essential_V2, want: true},
		{plan: commonv1.ServicePlan_Premium_Reserved, want: false},
	}

	for _, tc := range cases {
		if got := isSetDefaultResourcePoolServicePlan(tc.plan); got != tc.want {
			t.Fatalf("isSetDefaultResourcePoolServicePlan(%s) = %v, want %v", tc.plan.String(), got, tc.want)
		}
	}
}

func Test_validateSetDefaultResourcePoolServicePlanRules(t *testing.T) {
	ctx := context.Background()

	sharedEssential := &coresvccli.MgmtService{ResourcePool: &fakeResourcePoolServiceClient{
		resourcePool: &nextgenv1.ResourcePoolProfile{ResourcePool: &nextgenv1.ResourcePool{
			ServicePlan: commonv1.ServicePlan_Essential_V2,
			OwnerType:   nextgenv1.ResourcePool_SHARED,
			OwnerId:     0,
		}},
	}}
	if err := validateSetDefaultResourcePoolServicePlan(ctx, sharedEssential, 1, 0, "setting default resource pool"); err != nil {
		t.Fatalf("essential_v2 org=0 should pass, got %v", err)
	}
	if err := validateSetDefaultResourcePoolServicePlan(ctx, sharedEssential, 1, 42, "setting default resource pool"); err != nil {
		t.Fatalf("essential_v2 org!=0 should pass, got %v", err)
	}

	orgPremium := &coresvccli.MgmtService{ResourcePool: &fakeResourcePoolServiceClient{
		resourcePool: &nextgenv1.ResourcePoolProfile{ResourcePool: &nextgenv1.ResourcePool{
			ServicePlan: commonv1.ServicePlan_Premium,
			OwnerType:   nextgenv1.ResourcePool_ORGANIZATION,
			OwnerId:     7,
		}},
	}}
	if err := validateSetDefaultResourcePoolServicePlan(ctx, orgPremium, 1, 7, "setting default resource pool"); err != nil {
		t.Fatalf("premium with matching org should pass, got %v", err)
	}
	if err := validateSetDefaultResourcePoolServicePlan(ctx, orgPremium, 1, 0, "setting default resource pool"); err == nil {
		t.Fatalf("premium with org=0 should fail")
	}
	if err := validateSetDefaultResourcePoolServicePlan(ctx, orgPremium, 1, 8, "setting default resource pool"); err == nil {
		t.Fatalf("premium with owner mismatch should fail")
	}
}

func Test_createResourcePool_getParam_withHighAvailabilityType(t *testing.T) {
	tests := []struct {
		value string
		want  nextgenv1.ResourcePool_HighAvailabilityType
	}{
		{value: "REGIONAL", want: nextgenv1.ResourcePool_REGIONAL},
		{value: "ZONAL", want: nextgenv1.ResourcePool_ZONAL},
	}

	for _, tt := range tests {
		t.Run(tt.value, func(t *testing.T) {
			params := baseCreateResourcePoolParams()
			params[opent.ParamHighAvailabilityType] = tt.value

			got, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(params))
			if err != nil {
				t.Fatalf("getParam() error = %v", err)
			}
			if got.GetResourcePool().GetHighAvailabilityType() != tt.want {
				t.Fatalf("high availability type = %s, want %s", got.GetResourcePool().GetHighAvailabilityType(), tt.want)
			}
			if got.GetResourcePool().GetZoneConfig() != nil {
				t.Fatalf("zone config = %v, want nil when only HA provided", got.GetResourcePool().GetZoneConfig())
			}
		})
	}
}

func Test_createResourcePool_getParam_withNodeZones(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamNodeZones] = "us-west-2a,us-west-2b,us-west-2c"

	got, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	expectNodeZones(t, got.GetResourcePool().GetZoneConfig().GetNodeZones(), []string{"us-west-2a", "us-west-2b", "us-west-2c"})
	if got.GetResourcePool().GetHighAvailabilityType() != nextgenv1.ResourcePool_HIGH_AVAILABILITY_TYPE_UNSPECIFIED {
		t.Fatalf("high availability type = %s, want UNSPECIFIED", got.GetResourcePool().GetHighAvailabilityType())
	}
}

func Test_createResourcePool_getParam_withHighAvailabilityTypeAndNodeZones(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamHighAvailabilityType] = "REGIONAL"
	params[opent.ParamNodeZones] = "us-west-2a,us-west-2b,us-west-2c"

	got, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	if got.GetResourcePool().GetHighAvailabilityType() != nextgenv1.ResourcePool_REGIONAL {
		t.Fatalf("high availability type = %s, want REGIONAL", got.GetResourcePool().GetHighAvailabilityType())
	}
	expectNodeZones(t, got.GetResourcePool().GetZoneConfig().GetNodeZones(), []string{"us-west-2a", "us-west-2b", "us-west-2c"})
}

func Test_createResourcePool_getParam_rejectsInvalidHighAvailabilityType(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamHighAvailabilityType] = "GLOBAL"

	_, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(params))
	if err == nil {
		t.Fatal("getParam() expected error, got nil")
	}
	if !strings.Contains(err.Error(), string(opent.ParamHighAvailabilityType)) {
		t.Fatalf("getParam() error = %q, want contains %q", err.Error(), opent.ParamHighAvailabilityType)
	}
}

func Test_createResourcePool_getParam_nodeZonesTrimsBlankEntries(t *testing.T) {
	params := baseCreateResourcePoolParams()
	params[opent.ParamNodeZones] = "us-west-2a, us-west-2b,  ,us-west-2c,"

	got, err := (&createResourcePool{}).getParam(buildCreateResourcePoolInstance(params))
	if err != nil {
		t.Fatalf("getParam() error = %v", err)
	}

	expectNodeZones(t, got.GetResourcePool().GetZoneConfig().GetNodeZones(), []string{"us-west-2a", "us-west-2b", "us-west-2c"})
}

func expectNodeZones(t *testing.T, got []string, want []string) {
	t.Helper()

	if len(got) != len(want) {
		t.Fatalf("node zones = %v, want %v", got, want)
	}
	for i := range want {
		if got[i] != want[i] {
			t.Fatalf("node zones = %v, want %v", got, want)
		}
	}
}
