package cluster

import (
	"testing"

	"github.com/onsi/gomega"
	regionaldedicatedv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"go.uber.org/mock/gomock"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/account/mock_account"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/billing/mock_billing"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/clusterconfig/mock_clusterconfig"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/clustermetric/mock_clustermetric"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/regionserver/mock_regionserver"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/remoteencryptor/mock_remoteencryptor"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func TestCvtToGlobalNodeInfo(t *testing.T) {
	ctrl := gomock.NewController(t, gomock.WithOverridableExpectations())

	encryptor := mock_remoteencryptor.NewMockClient(ctrl)
	accountCli := mock_account.NewMockClient(ctrl)
	regionalCli := mock_regionserver.NewMockClient(ctrl)
	metricCli := mock_clustermetric.NewMockClient(ctrl)

	billingCli := mock_billing.NewMockClient(ctrl)
	clusterConfigCli := mock_clusterconfig.NewMockClient(ctrl)

	svc := newTestService(encryptor, accountCli, regionalCli, metricCli, billingCli, clusterConfigCli, true)
	g := gomega.NewWithT(t)
	tests := []struct {
		name      string
		nodeInfos *regionaldedicatedv1.GetComponentNodeInfosResponse
		cluster   *entity.Cluster
		expected  func(result *globaldedicatedv1.ComponentNodeInfo, err bizerr.BizError)
	}{
		{
			name: "normal node info",
			nodeInfos: &regionaldedicatedv1.GetComponentNodeInfosResponse{
				ComponentNodeInfos: []*regionaldedicatedv1.ComponentNodeInfo{
					{
						ComponentType: regionaldedicatedv1.ComponentType_TIDB,
						Nodes: []*regionaldedicatedv1.ComponentNodeInfo_Node{
							{
								InstanceType:      "t2.micro",
								State:             regionaldedicatedv1.ComponentNodeInfo_Node_NORMAL,
								StorageSizeGi:     0,
								StorageIops:       0,
								StorageThroughput: 0,
								AvailabilityZone:  "us-west-1a",
							},
						},
						TidbGroupId: "1",
					},
					{
						ComponentType: regionaldedicatedv1.ComponentType_TIPROXY,
						Nodes: []*regionaldedicatedv1.ComponentNodeInfo_Node{
							{
								InstanceType:      "t2.micro",
								State:             regionaldedicatedv1.ComponentNodeInfo_Node_NORMAL,
								StorageSizeGi:     0,
								StorageIops:       0,
								StorageThroughput: 0,
								AvailabilityZone:  "us-west-1a",
							},
						},
						TidbGroupId: "1",
					},
					{
						ComponentType: regionaldedicatedv1.ComponentType_TIFLASH,
						Nodes: []*regionaldedicatedv1.ComponentNodeInfo_Node{
							{
								InstanceType:      "t2.micro",
								State:             regionaldedicatedv1.ComponentNodeInfo_Node_NORMAL,
								StorageSizeGi:     0,
								StorageIops:       0,
								StorageThroughput: 0,
								AvailabilityZone:  "us-west-1a",
							},
						},
					},
				},
			},
			cluster: &entity.Cluster{
				Cluster: &globaldedicatedv1.Cluster{
					ClusterComponents: []*globaldedicatedv1.Cluster_ClusterComponent{
						{
							ComponentType: globaldedicatedv1.ComponentType_TIDB,
							SpuName:       "2C4G",
							TidbGroupId:   1,
						},
						{
							ComponentType: globaldedicatedv1.ComponentType_TIPROXY,
							SpuName:       "2C4G",
							TidbGroupId:   1,
						},
						{
							ComponentType: globaldedicatedv1.ComponentType_TIFLASH,
							SpuName:       "2C4G",
						},
					},
				},
			},
			expected: func(result *globaldedicatedv1.ComponentNodeInfo, err bizerr.BizError) {
				g.Expect(err).To(gomega.BeNil())
				g.Expect(result).To(gomega.Not(gomega.BeNil()))
				g.Expect(len(result.GetTidb())).To(gomega.Equal(1))
				g.Expect(len(result.GetTiproxy())).To(gomega.Equal(1))
				g.Expect(len(result.GetTiflash().GetNodes())).To(gomega.Equal(1))
			},
		},
		{
			name: "with empty tiproxy node and tiflash node",
			nodeInfos: &regionaldedicatedv1.GetComponentNodeInfosResponse{
				ComponentNodeInfos: []*regionaldedicatedv1.ComponentNodeInfo{
					{
						ComponentType: regionaldedicatedv1.ComponentType_TIPROXY,
						Nodes:         []*regionaldedicatedv1.ComponentNodeInfo_Node{},
						TidbGroupId:   "1",
					},
					{
						ComponentType: regionaldedicatedv1.ComponentType_TIFLASH,
						Nodes:         []*regionaldedicatedv1.ComponentNodeInfo_Node{},
					},
				},
			},
			cluster: &entity.Cluster{
				Cluster: &globaldedicatedv1.Cluster{},
			},
			expected: func(result *globaldedicatedv1.ComponentNodeInfo, err bizerr.BizError) {
				g.Expect(err).To(gomega.BeNil())
				g.Expect(result).To(gomega.Not(gomega.BeNil()))
				g.Expect(len(result.GetTiproxy())).To(gomega.Equal(0))
				g.Expect(result.GetTiflash()).To(gomega.BeNil())
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			result, err := svc.cvtToGlobalNodeInfo(t.Context(), test.nodeInfos, test.cluster)
			if test.expected != nil {
				test.expected(result, err)
			}
		})
	}
}
