package main

import (
	"context"
	"fmt"
	"math/rand"
	"strconv"
	"testing"
	"time"

	"github.com/spf13/cobra"
	"github.com/stretchr/testify/assert"
	pb "github.com/tidbcloud/config-center/proto/config_center"
	"google.golang.org/grpc"
	"google.golang.org/protobuf/types/known/structpb"
	"k8s.io/utils/ptr"
)

// testCmd represents the test command
var testCmd = &cobra.Command{
	Use:   "test",
	Short: "run test for config-center",
	Long:  `run test for config-center`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("test called")
		var opts []grpc.DialOption
		opts = append(opts, grpc.WithInsecure())
		conn, err := grpc.Dial("localhost:8080", opts...)
		if err != nil {
			panic(err)
		}
		defer conn.Close()
		client := pb.NewConfigCenterServiceClient(conn)
		t := prepareTemplates(nil, client)
		testConfigCenterService(client, t)
		testInsertParamDict(client)
	},
}

func init() {
	rootCmd.AddCommand(testCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// testCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// testCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

func prepareTemplates(t *testing.T, c pb.ConfigCenterServiceClient) []int64 {
	assert := assert.New(t)
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	templateIDs := []int64{}

	t1, err := c.CreateConfigurationTemplate(ctx, &pb.CreateConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb.toml",
		TemplateName:      "tidb",
		Data: map[string]*pb.ParamItem{
			"log.level": {
				Value:   structpb.NewStringValue("template0"),
				Comment: "error log level is template0",
			},
			"wtf": {
				Value:   structpb.NewStringValue("wtf"),
				Comment: "wtf",
			},
		},
	})
	assert.Nil(err)
	templateIDs = append(templateIDs, t1.GetTemplateId())

	t2, err := c.CreateConfigurationTemplate(ctx, &pb.CreateConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb.toml",
		TemplateName:      "tidb",
		Data: map[string]*pb.ParamItem{
			"log.level": {
				Value:   structpb.NewStringValue("template1"),
				Comment: "error log level is template0",
			},
			"log.test": {
				Value:   structpb.NewStringValue("test"),
				Comment: "test",
			},
		},
	})
	assert.Nil(err)
	templateIDs = append(templateIDs, t2.GetTemplateId())

	return templateIDs
}

func testConfigCenterService(c pb.ConfigCenterServiceClient, templateIDs []int64) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	warn, _ := structpb.NewValue("warn")
	clusterID := strconv.Itoa(rand.Int())
	req, err := c.CreateClusterConfigurationFile(ctx, &pb.CreateClusterConfigurationFileRequest{
		ClusterType: pb.ClusterType_DEDICATED_TIDB,
		ClusterId:   clusterID,
		ConfigurationFile: &pb.ConfigurationFile{
			ConfigurationFilename: "tidb.toml",
			Format:                pb.Format_YAML,
			TemplateId:            templateIDs[0],
			CustomParams: map[string]*pb.ParamItem{
				"log.level": {
					Value:   warn,
					Comment: "warn log level",
				},
			},
		},
	})

	if err != nil {
		panic(err)
	}

	fmt.Println(req.GetTemplateId())
	fmt.Println(string(req.Text))

	resp2, err := c.GetClusterConfigurationFile(ctx, &pb.GetClusterConfigurationFileRequest{
		ClusterType:           pb.ClusterType_DEDICATED_TIDB,
		ClusterId:             clusterID,
		ConfigurationFilename: "tidb.toml",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(resp2.GetTemplateId())
	fmt.Println(string(resp2.Text))

	resp3, err := c.UpdateClusterConfigurationFile(ctx, &pb.UpdateClusterConfigurationFileRequest{
		ClusterType:           pb.ClusterType_DEDICATED_TIDB,
		ClusterId:             clusterID,
		ConfigurationFilename: "tidb.toml",
		TemplateId:            &templateIDs[1],
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(resp3.GetTemplateId())
	fmt.Println(string(resp3.Text))
}

func testInsertParamDict(c pb.ConfigCenterServiceClient) {
	items := make([]*pb.ConfigurationDictItem, 0)
	v, err := structpb.NewValue(false)
	if err != nil {
		panic(err)
	}
	reloadable := false
	items = append(items, &pb.ConfigurationDictItem{
		ConfigurationType: "tidb",
		Version:           "v7.5.0",
		ParamKey:          "test",
		DefaultValue:      v,
		Type:              pb.ConfigParamType_bool,
		HotReloadable:     &reloadable,
	})
	c.BatchCreateConfigurationDictItems(context.Background(), &pb.BatchCreateConfigurationDictRequest{ConfigurationDictItems: items})
}

func createTidbCluster(c pb.ConfigCenterServiceClient, clusterID string) (*pb.ClusterMeta, error) {
	// 1. check exist
	exist, err := c.GetClusterMeta(context.Background(), &pb.GetClusterMetaRequest{
		ClusterType: pb.ClusterType_DEDICATED_TIDB,
		ClusterId:   clusterID,
	})
	if err == nil {
		return nil, fmt.Errorf("cluster already exists: %v", exist)
	}

	// 2. create
	_, err = c.AutoUpdateClusterMeta(context.Background(), &pb.AutoUpdateClusterMetaRequest{
		ClusterType: pb.ClusterType_DEDICATED_TIDB,
		ClusterId:   clusterID,
		Version:     ptr.To("v7.5.0"),
		ComponentRequests: []*pb.UpdateClusterComponentRequest{
			{
				ComponentName: "tidb",
				CloudProvider: ptr.To("aws"),
				NodeInstance:  ptr.To("m6g.xlarge"),
				AllowMissing:  true,
			},
			{
				ComponentName: "tikv",
				CloudProvider: ptr.To("aws"),
				NodeInstance:  ptr.To("m6g.xlarge"),
				AllowMissing:  true,
			},
			{
				ComponentName: "pd",
				CloudProvider: ptr.To("aws"),
				NodeInstance:  ptr.To("m6g.xlarge"),
				AllowMissing:  true,
			},
			{
				ComponentName: "tiflash",
				CloudProvider: ptr.To("aws"),
				NodeInstance:  ptr.To("m6g.xlarge"),
				AllowMissing:  true,
			},
		},
		ConfigurationFileRequests: []*pb.AutoUpdateClusterConfigurationFileRequest{
			{
				ConfigurationFilename: "tidb",
				ConfigurationType:     ptr.To("tidb"),
				CloudProvider:         "aws",
				NodeInstance:          "m6g.xlarge",
				Format:                ptr.To(pb.Format_TOML),
			},
			{
				ConfigurationFilename: "tikv",
				ConfigurationType:     ptr.To("tikv"),
				CloudProvider:         "aws",
				NodeInstance:          "m6g.xlarge",
				Format:                ptr.To(pb.Format_TOML),
			},
			{
				ConfigurationFilename: "pd",
				ConfigurationType:     ptr.To("pd"),
				CloudProvider:         "aws",
				NodeInstance:          "m6g.xlarge",
				Format:                ptr.To(pb.Format_TOML),
			},
		},
	})
	if err != nil {
		return nil, err
	}

	// 3. get api to return pb.ClusterMeta
	return c.GetClusterMeta(context.Background(), &pb.GetClusterMetaRequest{
		ClusterType: pb.ClusterType_DEDICATED_TIDB,
		ClusterId:  clusterID,
	})
}

func scaleUpTidb(c pb.ConfigCenterServiceClient, clusterID string) (*pb.ClusterMeta, error) {
	_, err := c.AutoUpdateClusterMeta(context.Background(), &pb.AutoUpdateClusterMetaRequest{
		ClusterType: pb.ClusterType_DEDICATED_TIDB,
		ClusterId:   clusterID,
		Version:     ptr.To("v7.5.0"),
		ComponentRequests: []*pb.UpdateClusterComponentRequest{
			{
				ComponentName:                   "tikv",
				CloudProvider:                   ptr.To("aws"),
				NodeInstance:                    ptr.To("m6g.2xlarge"),
				UpdateRelatedConfigurationFiles: true,
			},
		},
	})
	if err != nil {
		return nil, err
	}

	return c.GetClusterMeta(context.Background(), &pb.GetClusterMetaRequest{
		ClusterType: pb.ClusterType_DEDICATED_TIDB,
		ClusterId:   "CCE2E-1234",
	})
}
