package main

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"

	"braces.dev/errtrace"
	"github.com/spf13/cobra"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"go.uber.org/zap"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/utilities/enum"
)

var serverAddr = "0.0.0.0:10019"

var (
	clusterId            string
	providerName         string
	regionName           string
	regionServiceAddress string
	regionEngineType     string
	oemProfileRef        string
	sys9AuthMode         string
	tokenTTLSec          uint32
	sessionRefreshSec    uint32
	retryPolicyRef       string
	regionId             uint64

	needDeleted bool

	cmd = &cobra.Command{
		Use:               "serverless-global-control-cli",
		CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
	}

	cloudProviderCmd = &cobra.Command{
		Use:   "provider",
		Short: "provider",
	}
	addCloudProviderCmd = &cobra.Command{
		Use:   "add",
		Short: "add",
		Run: func(cmd *cobra.Command, args []string) {
			cobra.CheckErr(createCloudProvider())
		},
	}

	regionCmd = &cobra.Command{
		Use:   "region",
		Short: "region",
	}
	addRegionCmd = &cobra.Command{
		Use:   "add",
		Short: "add",
		Run: func(cmd *cobra.Command, args []string) {
			cobra.CheckErr(createRegion())
		},
	}
	updateRegionCmd = &cobra.Command{
		Use:   "update",
		Short: "update",
		Run: func(cmd *cobra.Command, args []string) {
			cobra.CheckErr(updateRegion())
		},
	}
	listRegionCmd = &cobra.Command{
		Use:   "list",
		Short: "list",
		Run: func(cmd *cobra.Command, args []string) {
			cobra.CheckErr(listRegion())
		},
	}

	clusterCmd = &cobra.Command{
		Use:   "cluster",
		Short: "cluster",
	}
	addClusterCmd = &cobra.Command{
		Use:   "add",
		Short: "add",
		Run: func(cmd *cobra.Command, args []string) {
			cobra.CheckErr(createCluster())
		},
	}
	listClusterCmd = &cobra.Command{
		Use:   "list",
		Short: "list",
		Run: func(cmd *cobra.Command, args []string) {
			cobra.CheckErr(listCluster())
		},
	}
	deleteClusterCmd = &cobra.Command{
		Use:   "delete",
		Short: "delete",
		Run: func(cmd *cobra.Command, args []string) {
			cobra.CheckErr(deleteCluster())
		},
	}

	updateClusterCmd = &cobra.Command{
		Use:   "update",
		Short: "update",
		Run: func(cmd *cobra.Command, args []string) {
			cobra.CheckErr(updateCluster())
		},
	}
)

// provide cli to interact with the service.
func main() {
	// test code
	addCloudProviderCmd.PersistentFlags().StringVar(&providerName, "provider", "aws", "cloud provider name")
	if err := addCloudProviderCmd.MarkPersistentFlagRequired("provider"); err != nil {
		panic(err)
	}
	cloudProviderCmd.AddCommand(addCloudProviderCmd)
	addRegionCmd.PersistentFlags().StringVar(&regionServiceAddress, "region-svc-addr", "", "region service address")
	addRegionCmd.PersistentFlags().StringVar(&regionEngineType, "engine-type", "MYSQL_COMPATIBLE", "region engine type")
	addRegionCmd.PersistentFlags().StringVar(&oemProfileRef, "oem-profile-ref", "", "sys9 oem profile ref")
	addRegionCmd.PersistentFlags().StringVar(&sys9AuthMode, "sys9-auth-mode", "", "sys9 auth mode")
	addRegionCmd.PersistentFlags().Uint32Var(&tokenTTLSec, "token-ttl-sec", 0, "sys9 token ttl in seconds")
	addRegionCmd.PersistentFlags().Uint32Var(&sessionRefreshSec, "session-refresh-sec", 0, "sys9 session refresh before seconds")
	addRegionCmd.PersistentFlags().StringVar(&retryPolicyRef, "retry-policy-ref", "", "sys9 retry policy ref")
	regionCmd.AddCommand(addRegionCmd)
	updateRegionCmd.PersistentFlags().StringVar(&regionServiceAddress, "region-svc-addr", "", "region service address")
	updateRegionCmd.PersistentFlags().StringVar(&regionEngineType, "engine-type", "MYSQL_COMPATIBLE", "region engine type")
	updateRegionCmd.PersistentFlags().StringVar(&oemProfileRef, "oem-profile-ref", "", "sys9 oem profile ref")
	updateRegionCmd.PersistentFlags().StringVar(&sys9AuthMode, "sys9-auth-mode", "", "sys9 auth mode")
	updateRegionCmd.PersistentFlags().Uint32Var(&tokenTTLSec, "token-ttl-sec", 0, "sys9 token ttl in seconds")
	updateRegionCmd.PersistentFlags().Uint32Var(&sessionRefreshSec, "session-refresh-sec", 0, "sys9 session refresh before seconds")
	updateRegionCmd.PersistentFlags().StringVar(&retryPolicyRef, "retry-policy-ref", "", "sys9 retry policy ref")
	updateRegionCmd.PersistentFlags().Uint64Var(&regionId, "region-id", 0, "region id")
	regionCmd.AddCommand(updateRegionCmd)
	regionCmd.AddCommand(listRegionCmd)
	if err := addClusterCmd.MarkPersistentFlagRequired("provider"); err != nil {
		panic(err)
	}
	if err := addClusterCmd.MarkPersistentFlagRequired("region"); err != nil {
		panic(err)
	}
	if err := addClusterCmd.MarkPersistentFlagRequired("cluster-id"); err != nil {
		panic(err)
	}
	addClusterCmd.PersistentFlags().StringVar(&providerName, "provider", "aws", "cloud provider name")
	addClusterCmd.PersistentFlags().StringVar(&regionName, "region", "us-west-2", "region name")
	addClusterCmd.PersistentFlags().StringVar(&clusterId, "cluster-id", "", "cluster id")
	clusterCmd.AddCommand(addClusterCmd)
	listClusterCmd.PersistentFlags().BoolVar(&needDeleted, "need-deleted", false, "need to list deleted cluster")
	listClusterCmd.PersistentFlags().StringVar(&clusterId, "cluster-id", "", "cluster id")
	clusterCmd.AddCommand(listClusterCmd)
	if err := deleteClusterCmd.MarkPersistentFlagRequired("cluster-id"); err != nil {
		panic(err)
	}
	deleteClusterCmd.PersistentFlags().StringVar(&clusterId, "cluster-id", "", "cluster id")
	clusterCmd.AddCommand(deleteClusterCmd)
	if err := updateClusterCmd.MarkPersistentFlagRequired("cluster-id"); err != nil {
		panic(err)
	}
	updateClusterCmd.PersistentFlags().StringVar(&clusterId, "cluster-id", "", "cluster id")
	clusterCmd.AddCommand(updateClusterCmd)
	cmd.PersistentFlags().StringVar(&serverAddr, "grpc-server-addr", "0.0.0.0:10019", "grpc server address")
	cmd.AddCommand(cloudProviderCmd)
	cmd.AddCommand(regionCmd)
	cmd.AddCommand(clusterCmd)

	if err := cmd.Execute(); err != nil {
		panic(err)
	}
}

func createCluster() error {
	conn, err := tigrpc.NewClientConn(serverAddr)
	if err != nil {
		return errtrace.Wrap(err)
	}
	ctx := context.Background()

	cli := globalserverlessv1.NewServerlessServiceClient(conn)
	_, err = cli.Create(ctx, &globalserverlessv1.ServerlessServiceCreateRequest{
		Id:            clusterId,
		TenantId:      60205,
		CloudProvider: providerName,
		Region:        regionName,
		SpendLimit: &globalserverlessv1.SpendLimit{
			Monthly: 10000,
		},
		ProjectId: 1753469,
	})

	return errtrace.Wrap(err)
}

func listCluster() error {
	conn, err := tigrpc.NewClientConn(serverAddr)
	if err != nil {
		return errtrace.Wrap(err)
	}

	req := &globalserverlessv1.ServerlessServiceListRequest{
		Filter: &globalserverlessv1.ClusterFilter{
			Deleted: &needDeleted,
		},
	}
	if clusterId != "" {
		req.Filter.ClusterIds = []string{clusterId}
	}
	ctx := context.Background()
	cli := globalserverlessv1.NewServerlessServiceClient(conn)
	resp, err := cli.List(ctx, req)
	if err != nil {
		panic(err)
	}

	for _, item := range resp.Items {
		data, err := json.Marshal(item)
		if err != nil {
			return errtrace.Wrap(err)
		}
		fmt.Println(string(data))
	}

	return nil
}

func updateCluster() error {
	conn, err := tigrpc.NewClientConn(serverAddr)
	if err != nil {
		return errtrace.Wrap(err)
	}
	ctx := context.Background()
	cli := globalserverlessv1.NewServerlessServiceClient(conn)
	resp, err := cli.Update(ctx, &globalserverlessv1.ServerlessServiceUpdateRequest{
		Cluster: &globalserverlessv1.ServerlessServiceCluster{
			ClusterId: clusterId,
			Cluster: &regionalserverlessv1.ServerlessRegionalServiceClusterInfo{
				ClusterId: clusterId,
				Endpoints: &regionalserverlessv1.Endpoints{
					Private: &regionalserverlessv1.PrivateEndpoint{
						Disabled: true,
					},
				},
			},
		},
	})
	if err != nil {
		return errtrace.Wrap(err)
	}

	tilog.Info(ctx, "delete cluster successful", zap.Any("resp", resp))
	return nil
}

func deleteCluster() error {
	if clusterId == "" {
		return errtrace.Wrap(errors.New("empty cluster id"))
	}
	conn, err := tigrpc.NewClientConn(serverAddr)
	if err != nil {
		return errtrace.Wrap(err)
	}
	ctx := context.Background()
	cli := globalserverlessv1.NewServerlessServiceClient(conn)
	resp, err := cli.Delete(ctx, &globalserverlessv1.ServerlessServiceDeleteRequest{
		ClusterId: clusterId,
	})
	if err != nil {
		return errtrace.Wrap(err)
	}

	tilog.Info(ctx, "delete cluster successful", zap.Any("resp", resp))
	return nil
}

func createCloudProvider() error {
	conn, err := tigrpc.NewClientConn(serverAddr)
	if err != nil {
		return errtrace.Wrap(err)
	}
	ctx := context.Background()

	cli := globalserverlessv1.NewCloudProviderServiceClient(conn)

	cloudProviderId, err := enum.FromString[globalcommonv1.CloudProvider_Name](providerName)
	if err != nil {
		return errtrace.Wrap(err)
	}

	resp, err := cli.Register(ctx, &globalserverlessv1.CloudProviderServiceRegisterRequest{
		CloudProvider: &globalcommonv1.CloudProvider{
			Id:          globalcommonv1.CloudProvider_Name(cloudProviderId),
			DisplayName: providerName,
			IsPublic:    true,
			State:       globalcommonv1.CloudProvider_ACTIVE,
		},
	})
	if err != nil {
		return errtrace.Wrap(err)
	}

	fmt.Println(resp)

	return nil
}

func createRegion() error {
	conn, err := tigrpc.NewClientConn(serverAddr)
	if err != nil {
		return errtrace.Wrap(err)
	}
	ctx := context.Background()

	cli := globalserverlessv1.NewRegionServiceClient(conn)

	regionId, err := enum.FromString[globalcommonv1.Region_Name](regionName)
	if err != nil {
		return errtrace.Wrap(err)
	}

	cloudProviderId, err := enum.FromString[globalcommonv1.CloudProvider_Name](providerName)
	if err != nil {
		return errtrace.Wrap(err)
	}

	resp, err := cli.Register(ctx, &globalserverlessv1.RegionServiceRegisterRequest{
		Region: &globalcommonv1.Region{
			Id:              globalcommonv1.Region_Name(regionId),
			DisplayName:     regionName,
			CloudProviderId: globalcommonv1.CloudProvider_Name(cloudProviderId),
			IsPublic:        true,
			State:           globalcommonv1.Region_ACTIVE,
			ServiceAddress:  regionServiceAddress,
			EngineType:      mustRegionEngineType(regionEngineType),
			Sys9Config: &globalcommonv1.RegionSys9Config{
				OemProfileRef:           oemProfileRef,
				AuthMode:                sys9AuthMode,
				TokenTtlSec:             tokenTTLSec,
				SessionRefreshBeforeSec: sessionRefreshSec,
				RetryPolicyRef:          retryPolicyRef,
			},
		},
	})
	if err != nil {
		return errtrace.Wrap(err)
	}

	fmt.Println(resp)

	return nil
}

func updateRegion() error {
	conn, err := tigrpc.NewClientConn(serverAddr)
	if err != nil {
		return errtrace.Wrap(err)
	}
	ctx := context.Background()

	cli := globalserverlessv1.NewRegionServiceClient(conn)

	regionId, err := enum.FromString[globalcommonv1.Region_Name](regionName)
	if err != nil {
		return errtrace.Wrap(err)
	}

	cloudProviderId, err := enum.FromString[globalcommonv1.CloudProvider_Name](providerName)
	if err != nil {
		return errtrace.Wrap(err)
	}

	resp, err := cli.Update(ctx, &globalserverlessv1.RegionServiceUpdateRequest{
		Region: &globalcommonv1.Region{
			Id:              globalcommonv1.Region_Name(regionId),
			DisplayName:     regionName,
			CloudProviderId: globalcommonv1.CloudProvider_Name(cloudProviderId),
			IsPublic:        true,
			State:           globalcommonv1.Region_ACTIVE,
			ServiceAddress:  regionServiceAddress,
			EngineType:      mustRegionEngineType(regionEngineType),
			Sys9Config: &globalcommonv1.RegionSys9Config{
				OemProfileRef:           oemProfileRef,
				AuthMode:                sys9AuthMode,
				TokenTtlSec:             tokenTTLSec,
				SessionRefreshBeforeSec: sessionRefreshSec,
				RetryPolicyRef:          retryPolicyRef,
			},
		},
	})
	if err != nil {
		return errtrace.Wrap(err)
	}

	fmt.Println(resp)

	return nil
}

func mustRegionEngineType(v string) globalcommonv1.Region_EngineType {
	engineType, err := enum.FromString[globalcommonv1.Region_EngineType](v)
	if err != nil {
		return globalcommonv1.Region_MYSQL_COMPATIBLE
	}
	return globalcommonv1.Region_EngineType(engineType)
}

func listRegion() error {
	conn, err := tigrpc.NewClientConn(serverAddr)
	if err != nil {
		return errtrace.Wrap(err)
	}
	ctx := context.Background()

	cli := globalserverlessv1.NewRegionServiceClient(conn)
	resp, err := cli.List(ctx, &globalserverlessv1.RegionServiceListRequest{})
	if err != nil {
		return errtrace.Wrap(err)
	}

	fmt.Println(resp)

	return nil
}
