package ngoperation

import (
	"context"
	"errors"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"go.uber.org/mock/gomock"
	"google.golang.org/grpc"

	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/infra/clients/coresvccli"
	"github.com/tidbcloud/devops/pkg/service/operation/actions"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

var _ = Describe("AddCassandraLabel", func() {
	baseInstance := func(params map[opent.ParamName]string) *opent.Instance {
		result := &opent.Instance{
			Operation: operationEnableCassandra,
			Params: map[opent.ParamName]string{
				opent.ParamClusterID: "1234567890",
			},
		}
		for k, v := range params {
			result.Params[k] = v
		}
		return result
	}

	newAction := func(clusterClient mgmtv1.ClusterServiceClient) *addCassandraLabel {
		return &addCassandraLabel{comps: &actions.Components{
			CoreSvc: &coresvccli.CoreSvcCli{
				Mgmt: &coresvccli.MgmtService{
					Cluster: clusterClient,
				},
			},
		}}
	}

	Describe("Metadata", func() {
		It("returns basic properties", func() {
			action := &addCassandraLabel{}
			meta := action.Metadata()
			Expect(meta.Name).To(Equal(opent.ActionName("add_cassandra_label")))
			Expect(meta.Type).To(Equal(opent.ActionTypeAuto))
		})
	})

	Describe("PreCheck", func() {
		It("rejects missing cluster id", func() {
			action := newAction(coresvccli.NewMockClusterServiceClient(gomock.NewController(GinkgoT())))

			err := action.PreCheck(context.Background(), &opent.Instance{Params: map[opent.ParamName]string{}})

			Expect(err).To(HaveOccurred())
		})

		It("rejects invalid cluster id", func() {
			action := newAction(coresvccli.NewMockClusterServiceClient(gomock.NewController(GinkgoT())))

			err := action.PreCheck(context.Background(), baseInstance(map[opent.ParamName]string{
				opent.ParamClusterID: "not-a-number",
			}))

			Expect(err).To(HaveOccurred())
		})

		It("rejects missing cluster client", func() {
			action := &addCassandraLabel{comps: &actions.Components{}}

			err := action.PreCheck(context.Background(), baseInstance(nil))

			Expect(err).To(HaveOccurred())
		})
	})

	Describe("Start", func() {
		It("adds the fixed cassandra label and verifies the returned labels", func() {
			ctrl := gomock.NewController(GinkgoT())
			clusterClient := coresvccli.NewMockClusterServiceClient(ctrl)
			clusterClient.EXPECT().
				AddClusterLabels(gomock.Any(), gomock.Any()).
				DoAndReturn(func(_ context.Context, req *mgmtv1.AddClusterLabelsRequest, _ ...grpc.CallOption) (*mgmtv1.Cluster, error) {
					Expect(req.GetClusterId()).To(Equal(uint64(1234567890)))
					Expect(req.GetLabels()).To(Equal(map[string]string{
						cassandraLabelKey: cassandraLabelValue,
					}))
					return &mgmtv1.Cluster{Labels: map[string]string{
						cassandraLabelKey: cassandraLabelValue,
					}}, nil
				})
			action := newAction(clusterClient)

			result, err := action.Start(context.Background(), baseInstance(nil))

			Expect(err).NotTo(HaveOccurred())
			Expect(result.Result).To(Equal(opent.ActionStatusSucceeded))
			Expect(result.ActionOutput).To(ContainSubstring("tidb.cloud/cassandra=true"))
		})

		It("returns failed when Global AddClusterLabels fails", func() {
			ctrl := gomock.NewController(GinkgoT())
			clusterClient := coresvccli.NewMockClusterServiceClient(ctrl)
			clusterClient.EXPECT().
				AddClusterLabels(gomock.Any(), gomock.Any()).
				Return(nil, errors.New("api failed"))
			action := newAction(clusterClient)

			result, err := action.Start(context.Background(), baseInstance(nil))

			Expect(err).NotTo(HaveOccurred())
			Expect(result.Result).To(Equal(opent.ActionStatusFailed))
			Expect(result.ActionOutput).To(ContainSubstring("api failed"))
		})

		It("returns failed when returned labels do not contain cassandra enabled", func() {
			ctrl := gomock.NewController(GinkgoT())
			clusterClient := coresvccli.NewMockClusterServiceClient(ctrl)
			clusterClient.EXPECT().
				AddClusterLabels(gomock.Any(), gomock.Any()).
				Return(&mgmtv1.Cluster{Labels: map[string]string{}}, nil)
			action := newAction(clusterClient)

			result, err := action.Start(context.Background(), baseInstance(nil))

			Expect(err).NotTo(HaveOccurred())
			Expect(result.Result).To(Equal(opent.ActionStatusFailed))
			Expect(result.ActionOutput).To(ContainSubstring("returned cluster labels"))
		})
	})
})
