package cluster

import (
	"encoding/json"
	"testing"

	. "github.com/onsi/gomega"

	"github.com/tidbcloud/devops/pkg/infra/clients/mgmthttpcli"
	commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"google.golang.org/protobuf/types/known/timestamppb"
)

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

	tests := []struct {
		name             string
		hiddenComponents []ComponentType
		wantComponents   []ComponentType
	}{
		{
			name: "legacy resource pool hides tidb and tiproxy",
			hiddenComponents: []ComponentType{
				ComponentTypeTidb,
				ComponentTypeTiproxy,
			},
			wantComponents: []ComponentType{
				ComponentType("TIKV"),
			},
		},
		{
			name: "shared resource pool hides tidb and keeps tiproxy",
			hiddenComponents: []ComponentType{
				ComponentTypeTidb,
			},
			wantComponents: []ComponentType{
				ComponentType("TIPROXY"),
				ComponentType("TIKV"),
			},
		},
		{
			name: "no hidden components preserves all components",
			wantComponents: []ComponentType{
				ComponentType("TIDB"),
				ComponentType("TIPROXY"),
				ComponentType("TIKV"),
			},
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			g := NewWithT(t)

			got := BuildSharedPool(testSharedPool(), tt.hiddenComponents...)

			g.Expect(got.ID).To(Equal("pool-1"))
			g.Expect(got.Phase).To(Equal("AVAILABLE"))
			gotComponents := make([]ComponentType, 0, len(got.ComponentGroups))
			for _, group := range got.ComponentGroups {
				gotComponents = append(gotComponents, group.ComponentType)
			}
			g.Expect(gotComponents).To(Equal(tt.wantComponents))
		})
	}
}

func TestBuildSharedPoolCopiesComponentDetails(t *testing.T) {
	t.Parallel()
	g := NewWithT(t)

	got := BuildSharedPool(testSharedPool(), ComponentTypeTidb, ComponentTypeTiproxy)

	g.Expect(got.ComponentGroups).To(HaveLen(1))
	tikv := got.ComponentGroups[0]
	g.Expect(tikv.Id).To(Equal("tikv-1"))
	g.Expect(tikv.ComponentType).To(Equal(ComponentType("TIKV")))
	g.Expect(tikv.Version).To(Equal("v8.5.0"))
	g.Expect(tikv.Phase).To(Equal("AVAILABLE"))
	g.Expect(tikv.NodeCount).To(Equal(uint32(3)))
	g.Expect(tikv.MaxNodeCount).To(Equal(uint32(6)))
	g.Expect(tikv.MinNodeCount).To(Equal(uint32(3)))
	g.Expect(tikv.CpuNum).To(Equal(uint32(4)))
	g.Expect(tikv.MemoryGi).To(Equal(uint32(16)))
	g.Expect(tikv.Storages).To(Equal([]*SharedPoolStorage{
		{
			Name:          "data",
			Type:          "io2",
			SizeGi:        512,
			Iops:          10000,
			ThroughputMbs: 500,
		},
	}))
}

func TestBuildClusterFromResourcePoolCopiesPdDeploymentMode(t *testing.T) {
	t.Parallel()
	g := NewWithT(t)

	got := BuildClusterFromResourcePool(testResourcePoolProfile(nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_MONOLITHIC))

	g.Expect(got.ResourcePoolDetails).NotTo(BeNil())
	g.Expect(got.ResourcePoolDetails.PdDeploymentMode).To(Equal(nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_MONOLITHIC.String()))
}

func TestBuildClusterFromResourcePoolOmitsUnspecifiedPdDeploymentMode(t *testing.T) {
	t.Parallel()
	g := NewWithT(t)

	got := BuildClusterFromResourcePool(testResourcePoolProfile(nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED))
	raw, err := json.Marshal(got.ResourcePoolDetails)

	g.Expect(err).NotTo(HaveOccurred())
	g.Expect(got.ResourcePoolDetails).NotTo(BeNil())
	g.Expect(got.ResourcePoolDetails.PdDeploymentMode).To(BeEmpty())
	g.Expect(string(raw)).NotTo(ContainSubstring("pd_deployment_mode"))
}

func TestBuildClusterFromResourcePoolCopiesEnabledTiKVRemoteWorkers(t *testing.T) {
	t.Parallel()
	g := NewWithT(t)

	got := BuildClusterFromResourcePool(testResourcePoolProfile(
		nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED,
		nextgenv1.ComponentType_TIKV_WORKER,
		nextgenv1.ComponentType_COPROCESSOR_WORKER,
	))

	g.Expect(got.ResourcePoolDetails).NotTo(BeNil())
	g.Expect(got.ResourcePoolDetails.EnabledTiKVRemoteWorkers).To(Equal([]string{
		nextgenv1.ComponentType_TIKV_WORKER.String(),
		nextgenv1.ComponentType_COPROCESSOR_WORKER.String(),
	}))
}

func TestBuildClusterFromResourcePoolCopiesDefaultOrgs(t *testing.T) {
	t.Parallel()
	g := NewWithT(t)

	got := BuildClusterFromResourcePool(testResourcePoolProfile(
		nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED,
	))

	g.Expect(got.ResourcePoolDetails).NotTo(BeNil())
	g.Expect(got.ResourcePoolDetails.DefaultOrgs).To(Equal([]string{"0", "1001"}))
}

func TestBuildClusterFromResourcePoolOmitsEmptyEnabledTiKVRemoteWorkers(t *testing.T) {
	t.Parallel()
	g := NewWithT(t)

	got := BuildClusterFromResourcePool(testResourcePoolProfile(nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED))
	raw, err := json.Marshal(got.ResourcePoolDetails)

	g.Expect(err).NotTo(HaveOccurred())
	g.Expect(got.ResourcePoolDetails).NotTo(BeNil())
	g.Expect(got.ResourcePoolDetails.EnabledTiKVRemoteWorkers).To(BeEmpty())
	g.Expect(string(raw)).NotTo(ContainSubstring("enabled_tikv_remote_workers"))
}

func testResourcePoolProfile(mode nextgenv1.PdDeploymentMode, enabledTiKVRemoteWorkers ...nextgenv1.ComponentType) *nextgenv1.ResourcePoolProfile {
	return &nextgenv1.ResourcePoolProfile{
		ResourcePool: &nextgenv1.ResourcePool{
			Id:          1,
			Name:        "rp-test",
			ProviderId:  commonv1.CloudProvider_AWS,
			RegionId:    commonv1.Region_AWS_US_EAST_1,
			OwnerType:   nextgenv1.ResourcePool_SHARED,
			ServicePlan: commonv1.ServicePlan_Premium,
			CreateTime:  timestamppb.Now(),
		},
		Region: &commonv1.Region{
			LogicalName: "us-east-1",
		},
		PdDeploymentMode:         mode,
		EnabledTikvRemoteWorkers: enabledTiKVRemoteWorkers,
		DefaultOrgs:              []string{"0", "1001"},
	}
}

func testSharedPool() *mgmthttpcli.SharedPool {
	return &mgmthttpcli.SharedPool{
		Id:    "pool-1",
		Phase: "AVAILABLE",
		ComponentGroups: []*mgmthttpcli.Component{
			{
				Id:            "tidb-1",
				ComponentType: "TIDB",
				Phase:         "AVAILABLE",
			},
			{
				Id:            "tiproxy-1",
				ComponentType: "TIPROXY",
				Phase:         "AVAILABLE",
				NodeQuota: &mgmthttpcli.NodeQuota{
					NodeSpec:  &mgmthttpcli.NodeSpec{Cpu: 2, MemoryGi: 4},
					NodeCount: 2,
				},
			},
			{
				Id:            "tikv-1",
				ComponentType: "TIKV",
				Version:       "v8.5.0",
				Phase:         "AVAILABLE",
				NodeQuota: &mgmthttpcli.NodeQuota{
					NodeSpec:     &mgmthttpcli.NodeSpec{Cpu: 4, MemoryGi: 16},
					NodeCount:    3,
					MaxNodeCount: 6,
					MinNodeCount: 3,
				},
				Storages: []*mgmthttpcli.Component_Storage{
					{
						Name:          "data",
						Type:          "io2",
						SizeGi:        512,
						Iops:          10000,
						ThroughputMbs: 500,
					},
				},
			},
		},
	}
}
