package dag

import (
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/fastflow/pkg/entity"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/workflow/action"
)

func TestBuildScaleTopologyWithGKEHeterogeneousDeployDoesNotPreDeleteInMgmt(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_GCP,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_TIKV.String(),
				NeedToScaleInstanceMu: &action.NeedToScaleInstanceMu{
					InstanceMuChanged: true,
					CurrentInstanceMu: "n2-standard-8",
					TargetInstanceMu:  "n2-standard-16",
				},
			},
		},
	}

	tasks, err := BuildScaleTopology(param, false)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	require.Equal(t, "scale_tikv_4", tasks[0].ID)
	require.Equal(t, action.ScaleComponentWithConfigOriginActionName, tasks[0].ActionName)
	require.Equal(t, action.PollClusterUntilSynced, tasks[1].ActionName)
	require.Equal(t, []string{tasks[0].ID}, tasks[1].DependOn)
	require.Equal(t, action.SetGlobalClusterStatus, tasks[2].ActionName)
	requireNoDeleteGKEHeterogeneousDeployBeforeScaleTask(t, tasks)
}

func TestBuildForceScaleTopologyWithGKEHeterogeneousDeployDoesNotPreDeleteInMgmt(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_GCP,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_TIKV.String(),
				NeedToScaleInstanceMu: &action.NeedToScaleInstanceMu{
					InstanceMuChanged: true,
					CurrentInstanceMu: "n2-standard-8",
					TargetInstanceMu:  "n2-standard-16",
				},
			},
		},
	}

	tasks, err := BuildScaleTopology(param, true)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	require.Equal(t, "scale_tikv_4", tasks[0].ID)
	require.Equal(t, action.ScaleComponentWithConfigOriginActionName, tasks[0].ActionName)
	require.Equal(t, action.PollClusterUntilSynced, tasks[1].ActionName)
	require.Equal(t, []string{tasks[0].ID}, tasks[1].DependOn)
	require.Equal(t, action.SetGlobalClusterStatus, tasks[2].ActionName)
	requireNoDeleteGKEHeterogeneousDeployBeforeScaleTask(t, tasks)
}

func TestBuildScaleTopologyDoesNotInjectPreDeleteWhenInstanceMuUnchanged(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_GCP,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_PD.String(),
				NeedToScaleInstanceMu: &action.NeedToScaleInstanceMu{
					InstanceMuChanged: false,
					CurrentInstanceMu: "n2-standard-8",
					TargetInstanceMu:  "n2-standard-16",
				},
			},
		},
	}

	tasks, err := BuildScaleTopology(param, false)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	require.Equal(t, action.ScaleComponentWithConfigOriginActionName, tasks[0].ActionName)
	require.Equal(t, action.PollClusterUntilSynced, tasks[1].ActionName)
	require.Equal(t, action.SetGlobalClusterStatus, tasks[2].ActionName)
}

func TestBuildScaleTopologyDoesNotPreDeleteWhenInstanceMuChangedRegardlessOfEquality(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_GCP,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_TIKV.String(),
				NeedToScaleInstanceMu: &action.NeedToScaleInstanceMu{
					InstanceMuChanged: true,
					CurrentInstanceMu: "n2-standard-8",
					TargetInstanceMu:  "n2-standard-8",
				},
			},
		},
	}

	tasks, err := BuildScaleTopology(param, false)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	require.Equal(t, action.ScaleComponentWithConfigOriginActionName, tasks[0].ActionName)
	require.Equal(t, action.PollClusterUntilSynced, tasks[1].ActionName)
	require.Equal(t, action.SetGlobalClusterStatus, tasks[2].ActionName)
	requireNoDeleteGKEHeterogeneousDeployBeforeScaleTask(t, tasks)
}

func TestBuildScaleTopologyDoesNotInjectPreDeleteForPD(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_GCP,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_PD.String(),
				NeedToScaleInstanceMu: &action.NeedToScaleInstanceMu{
					InstanceMuChanged: true,
					CurrentInstanceMu: "n2-standard-8",
					TargetInstanceMu:  "n2-standard-16",
				},
			},
		},
	}

	tasks, err := BuildScaleTopology(param, false)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	require.Equal(t, action.ScaleComponentWithConfigOriginActionName, tasks[0].ActionName)
	require.Equal(t, action.PollClusterUntilSynced, tasks[1].ActionName)
	require.Equal(t, action.SetGlobalClusterStatus, tasks[2].ActionName)
}

func TestBuildScaleTopologyDoesNotInjectPreDeleteForUnsupportedComponent(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_GCP,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED.String(),
				NeedToScaleInstanceMu: &action.NeedToScaleInstanceMu{
					InstanceMuChanged: true,
					CurrentInstanceMu: "n2-standard-8",
					TargetInstanceMu:  "n2-standard-16",
				},
			},
		},
	}

	tasks, err := BuildScaleTopology(param, false)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	require.Equal(t, action.ScaleComponentWithConfigOriginActionName, tasks[0].ActionName)
	require.Equal(t, action.PollClusterUntilSynced, tasks[1].ActionName)
	require.Equal(t, action.SetGlobalClusterStatus, tasks[2].ActionName)
}

func TestBuildScaleTopologyDoesNotPreDeleteWithScaleUpFromZero(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_GCP,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_TIKV.String(),
				NeedToScaleInstanceMu: &action.NeedToScaleInstanceMu{
					InstanceMuChanged: true,
					ScaleUpFromZero:   true,
				},
			},
		},
	}

	tasks, err := BuildScaleTopology(param, false)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	require.Equal(t, action.ScaleComponentWithConfigOriginActionName, tasks[0].ActionName)
	requireNoDeleteGKEHeterogeneousDeployBeforeScaleTask(t, tasks)
}

func TestBuildScaleTopologyNoCleanupWhenInstanceTypeUnchanged(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_GCP,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_TIKV.String(),
			},
		},
	}

	tasks, err := BuildScaleTopology(param, false)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	require.Equal(t, action.ScaleComponentWithConfigOriginActionName, tasks[0].ActionName)
}

func TestBuildScaleTopologyDoesNotInjectPreDeleteOnAWS(t *testing.T) {
	param := &ScaleDagTasks{
		TenantId:        1,
		ProjectId:       2,
		ClusterId:       3,
		CloudProviderId: globalcommonv1.CloudProvider_AWS,
		ComponentsAsConfigOrigin: []*action.ScaleComponentParameter{
			{
				ID:            4,
				ComponentType: globaldedicatedv1.ComponentType_TIKV.String(),
				NeedToScaleInstanceMu: &action.NeedToScaleInstanceMu{
					InstanceMuChanged: true,
					CurrentInstanceMu: "n2-standard-8",
					TargetInstanceMu:  "n2-standard-16",
				},
			},
		},
	}

	tasks, err := BuildScaleTopology(param, false)
	require.NoError(t, err)
	require.Len(t, tasks, 3)
	requireNoDeleteGKEHeterogeneousDeployBeforeScaleTask(t, tasks)
}

func requireNoDeleteGKEHeterogeneousDeployBeforeScaleTask(t *testing.T, tasks []entity.Task) {
	t.Helper()
	for _, task := range tasks {
		require.NotEqual(t, action.DeleteGKEHeterogeneousDeployBeforeScaleActionName, task.ActionName,
			"delete_gke_heterogeneous_deploy_before_scale must not be injected by scale DAG")
	}
}
