package cluster

import (
	"context"
	"strconv"
	"testing"
	"time"

	"github.com/onsi/gomega"
	accountsdk "github.com/tidbcloud/account/idl/pbgen/proto/account"
	"go.uber.org/mock/gomock"
	grpcmetadata "google.golang.org/grpc/metadata"

	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	openapidedicatedv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/dedicated/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/account/mock_account"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/repository/db"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/repository/schema"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/common"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func TestDeleteDedicatedCluster(t *testing.T) {
	g := gomega.NewWithT(t)

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockAccountCli := mock_account.NewMockClient(ctrl)
	mockAccountCli.EXPECT().
		GetProject(gomock.Any(), gomock.Any()).
		DoAndReturn(func(ctx context.Context, projectId uint64) (*accountsdk.Project, error) {
			return &accountsdk.Project{
				Id:       projectId,
				TenantId: 1, // org id
			}, nil
		}).
		AnyTimes()
	mockAccountCli.EXPECT().
		GetTenantThirdPartyAccountProviders(gomock.Any(), gomock.Any()).
		DoAndReturn(func(ctx context.Context, id uint64) ([]openapicommonv1beta1.Region_CloudProvider, error) {
			return []openapicommonv1beta1.Region_CloudProvider{}, nil
		}).
		AnyTimes()
	mockAccountCli.EXPECT().
		UpsertClusterRelations(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
		Return(nil).
		AnyTimes()
	mockAccountCli.EXPECT().
		DeleteClusterRelations(gomock.Any(), gomock.Any()).
		Return(nil).
		AnyTimes()

	service, err := initServiceWithFakeClients(mockAccountCli)
	g.Expect(err).ShouldNot(gomega.HaveOccurred())
	dbProvider := db.NewProvider(nil)

	t.Run("Delete should success", func(t *testing.T) {
		// Create a cluster first
		ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
			metadata.UserIDHeaderKey:           "1",
			metadata.OrgIDHeaderKey:            "1",
			metadata.DefaultProjectIDHeaderKey: "1",
			metadata.ProjectListHeaderKey:      "1,2,3",
			metadata.ClusterIDHeaderKey:        "1",
			metadata.IsCheckPermissionKey:      "true",
		}))

		req := &openapidedicatedv1beta1.CreateClusterRequest{
			Cluster: &openapidedicatedv1beta1.Cluster{
				DisplayName: "delete-test-1",
				RegionId:    "aws-us-west-2",
				TidbNodeSetting: &openapidedicatedv1beta1.Cluster_TidbNodeSetting{
					NodeSpecKey: "8C16G",
					TidbNodeGroups: []*openapidedicatedv1beta1.TidbNodeGroup{
						{
							NodeCount: 1,
						},
					},
				},
				TikvNodeSetting: &openapidedicatedv1beta1.Cluster_StorageNodeSetting{
					NodeSpecKey:   "8C16G",
					NodeCount:     3,
					StorageSizeGi: 50,
					StorageType:   openapidedicatedv1beta1.Cluster_StorageNodeSetting_Standard,
				},
				Labels: map[string]string{
					common.ProjectIDLabelKey: "1",
				},
			},
		}
		c, err := service.CreateCluster(ctx, req)
		g.Expect(err).ShouldNot(gomega.HaveOccurred())

		time.Sleep(3 * time.Second) // wait for the workflow dag to finish

		// Build delete request
		deleteReq := &openapidedicatedv1beta1.DeleteClusterRequest{ClusterId: c.ClusterId}

		id, err := strconv.ParseUint(deleteReq.ClusterId, 10, 64)
		g.Expect(err).ShouldNot(gomega.HaveOccurred())
		ctx = metadata.AppendToIncomingMeta(ctx, &metadata.RequestMeta{
			ClusterID: id,
		})
		cluster, err := service.DeleteCluster(ctx, deleteReq)

		g.Expect(err).ShouldNot(gomega.HaveOccurred())
		g.Expect(cluster).ShouldNot(gomega.BeNil())
		g.Expect(cluster.ClusterId).ShouldNot(gomega.BeEmpty())
		g.Expect(cluster.DisplayName).Should(gomega.Equal("delete-test-1"))
		g.Expect(cluster.Version).ShouldNot(gomega.BeEmpty())
		g.Expect(cluster.GetRegionDisplayName()).
			Should(gomega.Equal(fakeRegionDisplayName(cluster.GetRegionId())))

		// Check the cluster in database
		var record schema.Cluster

		// Find the soft deleted record
		err = dbProvider.ReadDB(t.Context()).
			Model(&schema.Cluster{}).
			Unscoped().
			Where("cluster_id = ?", cluster.ClusterId).
			Take(&record).
			Error

		g.Expect(err).ShouldNot(gomega.HaveOccurred())
		g.Expect(record).ShouldNot(gomega.BeNil())
		g.Expect(record.DeletedAt.Valid).Should(gomega.BeTrue())
	})
}
