package gkeheterogeneous

import (
	"testing"

	"github.com/stretchr/testify/require"
	regionaldedicatedv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

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

	req := &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{
		ComponentType:           globaldedicatedv1.ComponentType_TIDB,
		CurrentInstanceType:     "n2-standard-8",
		AlternativeInstanceType: "n2-standard-16",
	}

	err := validateApplyRequest(req)
	require.Error(t, err)
	require.Equal(t, bizerr.ErrCodeParams, err.ErrCode())
}

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

	req := &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{
		ComponentType:           globaldedicatedv1.ComponentType_TIKV,
		CurrentInstanceType:     "n2-standard-8",
		AlternativeInstanceType: "n2-standard-16",
	}

	require.NoError(t, validateApplyRequest(req))
}

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

	req := &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{
		ComponentType:           globaldedicatedv1.ComponentType_TIKV,
		CurrentInstanceType:     " n2-standard-8 ",
		AlternativeInstanceType: "\tn2-standard-16\n",
	}

	require.NoError(t, validateApplyRequest(req))
	require.Equal(t, "n2-standard-8", req.GetCurrentInstanceType())
	require.Equal(t, "n2-standard-16", req.GetAlternativeInstanceType())
}

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

	req := &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{
		ComponentType:           globaldedicatedv1.ComponentType_TIKV,
		CurrentInstanceType:     " n2-standard-8 ",
		AlternativeInstanceType: "n2-standard-8",
	}

	err := validateApplyRequest(req)
	require.Error(t, err)
	require.Equal(t, bizerr.ErrCodeParams, err.ErrCode())
}

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

	err := validateTidbGroupForComponent(globaldedicatedv1.ComponentType_TIDB, 0)
	require.Error(t, err)
	require.Equal(t, bizerr.ErrCodeParams, err.ErrCode())

	require.NoError(t, validateTidbGroupForComponent(globaldedicatedv1.ComponentType_TIDB, 1))
	require.NoError(t, validateTidbGroupForComponent(globaldedicatedv1.ComponentType_TIKV, 0))
}

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

	deploy, err := ToGlobalDeploy(nil, nil, "", "")

	require.Nil(t, deploy)
	require.Error(t, err)
	require.Equal(t, bizerr.ErrCodeRecordNotFound, err.ErrCode())
}

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

	deploy, err := ToGlobalDeploy(
		&regionaldedicatedv1.GKEHeterogeneousDeploy{
			TidbGroupId: "not-a-number",
		},
		nil,
		"",
		"",
	)

	require.Nil(t, deploy)
	require.Error(t, err)
	require.Equal(t, bizerr.ErrCodeRegionalAPI, err.ErrCode())
}

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

	deploy, err := ToGlobalDeploy(
		&regionaldedicatedv1.GKEHeterogeneousDeploy{
			Enabled:       true,
			ComponentType: regionaldedicatedv1.ComponentType_TIDB,
			InstanceTypes: []string{"n2-standard-8", "n2-standard-16"},
			TidbGroupId:   "123",
		},
		&regionaldedicatedv1.GKEHeterogeneousNodePool{
			InstanceType: "n2-standard-16",
			MaxNodeCount: 2,
		},
		"",
		"",
	)

	require.NoError(t, err)
	require.NotNil(t, deploy)
	require.Equal(t, uint64(123), deploy.GetTidbGroupId())
	require.Equal(t, globaldedicatedv1.ComponentType_TIDB, deploy.GetComponentType())
	require.Equal(t, "n2-standard-8", deploy.GetCurrentInstanceType())
	require.Equal(t, "n2-standard-16", deploy.GetAlternativeInstanceType())
}

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

	deploy, err := ToGlobalDeploy(
		&regionaldedicatedv1.GKEHeterogeneousDeploy{
			Enabled:       true,
			ComponentType: regionaldedicatedv1.ComponentType_TIDB,
			InstanceTypes: []string{"n2-custom-8-16384", "n2d-custom-8-16384"},
			TidbGroupId:   "456",
		},
		&regionaldedicatedv1.GKEHeterogeneousNodePool{
			InstanceType: "n2-custom-8-16384",
			MaxNodeCount: 3,
		},
		"",
		"",
	)

	require.NoError(t, err)
	require.NotNil(t, deploy)
	require.Equal(t, "n2-custom-8-16384", deploy.GetCurrentInstanceType())
	require.Equal(t, "n2d-custom-8-16384", deploy.GetAlternativeInstanceType())
}

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

	deploy, err := ToGlobalDeploy(
		&regionaldedicatedv1.GKEHeterogeneousDeploy{
			Enabled:       true,
			ComponentType: regionaldedicatedv1.ComponentType_TIKV,
			InstanceTypes: []string{"n1-standard-4", "n1-standard-8"},
			TidbGroupId:   "",
		},
		nil,
		"",
		"",
	)

	require.NoError(t, err)
	require.NotNil(t, deploy)
	require.Equal(t, "n1-standard-4", deploy.GetCurrentInstanceType())
	require.Equal(t, "n1-standard-8", deploy.GetAlternativeInstanceType())
}

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

	deploy, err := ToGlobalDeploy(
		&regionaldedicatedv1.GKEHeterogeneousDeploy{
			Enabled:       true,
			ComponentType: regionaldedicatedv1.ComponentType_TIKV,
			InstanceTypes: []string{"n1-standard-4", "n1-standard-8"},
			TidbGroupId:   "",
			Status:        regionaldedicatedv1.GKEHeterogeneousDeployStatus_GKE_HETEROGENEOUS_DEPLOY_STATUS_HETEROGENEOUS_READY,
			PodCounts: []*regionaldedicatedv1.GKEHeterogeneousDeployPodCount{
				{MachineType: "n1-standard-4", PodCount: 3},
				{MachineType: "n1-standard-8", PodCount: 5},
			},
		},
		nil,
		"",
		"",
	)

	require.NoError(t, err)
	require.NotNil(t, deploy)
	require.Len(t, deploy.GetPodCounts(), 2)
	require.Equal(t, "n1-standard-4", deploy.GetPodCounts()[0].GetMachineType())
	require.Equal(t, int32(3), deploy.GetPodCounts()[0].GetPodCount())
	require.Equal(t, "n1-standard-8", deploy.GetPodCounts()[1].GetMachineType())
	require.Equal(t, int32(5), deploy.GetPodCounts()[1].GetPodCount())
	require.Equal(t, "GKE_HETEROGENEOUS_DEPLOY_STATUS_HETEROGENEOUS_READY", deploy.GetStatus())
}

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

	req := &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{
		ComponentType:           globaldedicatedv1.ComponentType_PD,
		CurrentInstanceType:     "n2-standard-8",
		AlternativeInstanceType: "n2-standard-16",
	}

	err := validateApplyRequest(req)
	require.Error(t, err)
	require.Equal(t, bizerr.ErrCodeParams, err.ErrCode())
}

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

	req := &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{
		ComponentType:           globaldedicatedv1.ComponentType_TIKV,
		CurrentInstanceType:     "n2-standard-8",
		AlternativeInstanceType: "n2-standard-16",
	}

	err := validateApplyRequest(req)
	require.NoError(t, err)
}

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

	req := &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{
		ComponentType:           globaldedicatedv1.ComponentType_TIDB,
		CurrentInstanceType:     "n2-standard-8",
		AlternativeInstanceType: "n2-standard-16",
		TidbGroupId:             100,
	}

	err := validateApplyRequest(req)
	require.NoError(t, err)
}

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

	tests := []struct {
		name          string
		componentType globaldedicatedv1.ComponentType
		want          bool
	}{
		{"TIKV", globaldedicatedv1.ComponentType_TIKV, true},
		{"TIDB", globaldedicatedv1.ComponentType_TIDB, true},
		{"PD", globaldedicatedv1.ComponentType_PD, false},
		{"TIFLASH", globaldedicatedv1.ComponentType_TIFLASH, false},
		{"TIFLASH_LEARNER", globaldedicatedv1.ComponentType_TIFLASH_LEARNER, false},
		{"TIPROXY", globaldedicatedv1.ComponentType_TIPROXY, false},
		{"UNSPECIFIED", globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			require.Equal(t, tt.want, IsSupportedRegionalHeterogeneousComponentType(tt.componentType))
		})
	}
}
