package central

import (
	"fmt"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/samber/lo"
	centralapi "github.com/tidbcloud/core-svc/idl/pbgen/proto/central"

	openapidedicatedv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/dedicated/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tempo-wrapper/internal/api/types"
	apiutils "github.com/tidbcloud/tidb-management-service/tempo-wrapper/internal/api/utils"
	"github.com/tidbcloud/tidb-management-service/tempo-wrapper/pkg/grpc"
	coregrpc "github.com/tidbcloud/tidb-management-service/tempo-wrapper/pkg/grpc/core_portal"
	mgmtgrpc "github.com/tidbcloud/tidb-management-service/tempo-wrapper/pkg/grpc/mgmt"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func ScaleCluster(ctx *gin.Context) {
	req := &centralapi.ScaleClusterReq{}
	err := types.ShouldBindJSON(ctx, req)
	if err != nil {
		types.BadRequestResp(ctx, fmt.Errorf("bind request body failed: %w", err))
		return
	}

	// check org/project id
	orgId, err := strconv.ParseUint(ctx.Param("org_id"), 10, 64)
	if err != nil {
		types.UnauthorizedResp(ctx, err)
		return
	}
	projectId, err := strconv.ParseUint(ctx.Param("project_id"), 10, 64)
	if err != nil {
		types.UnauthorizedResp(ctx, err)
		return
	}
	clusterId, err := strconv.ParseUint(ctx.Param("cluster_id"), 10, 64)
	if err != nil {
		types.BadRequestResp(ctx, fmt.Errorf("invalid cluster id: %w", err))
		return
	}

	// set org and project id
	req.OrgId = orgId
	req.ProjectId = projectId
	req.ClusterId = clusterId

	// check is new serverless cluster
	if ok, err := apiutils.IsNewServerlessCluster(ctx, clusterId, projectId, orgId); err != nil {
		types.InternalErrorResp(ctx, fmt.Errorf("check cluster failed: %w", err))
		return
	} else if ok {
		// is serverless cluster
		types.SuccessResp(ctx, &centralapi.DefaultResp{
			BaseResp: &centralapi.BaseResp{
				ErrCode: int64(bizerr.ErrCodePermission),
				ErrMsg:  "serverless cluster does not support scale",
			},
		})
		return
	}

	// check is new dedicated cluster
	if ok, err := apiutils.IsNewDedicatedCluster(ctx, clusterId, projectId, orgId); err != nil {
		types.InternalErrorResp(ctx, fmt.Errorf("check cluster failed: %w", err))
		return
	} else if ok { // is new dedicated cluster
		scaleReq := &openapidedicatedv1beta1.UpdateClusterRequest_Cluster{
			ClusterId: strconv.FormatUint(clusterId, 10),
		}

		for _, c := range req.Components {
			switch c.ComponentType {
			case centralapi.ComponentType_COMPONENT_TYPE_TIDB:
				if c.Replicas <= 0 {
					types.BizErrorRequestResp(ctx, bizerr.New(bizerr.ErrCodeInvalidNodeQuantity, "invalid tidb node quantity"))
					return
				}

				if c.NodeProfileName == "" {
					types.BizErrorRequestResp(ctx, bizerr.New(bizerr.ErrCodeInvalidNodeSize, "invalid tidb node profile name"))
					return
				}
				scaleReq.TidbNodeSetting = &openapidedicatedv1beta1.UpdateClusterRequest_TidbNodeSetting{
					NodeSpecKey: c.NodeProfileName,
					TidbNodeGroups: []*openapidedicatedv1beta1.UpdateClusterRequest_TidbNodeSetting_TidbNodeGroup{
						{
							NodeCount: lo.ToPtr(c.Replicas),
						},
					},
				}
			case centralapi.ComponentType_COMPONENT_TYPE_TIKV:
				if c.Replicas <= 0 {
					types.BizErrorRequestResp(ctx, bizerr.New(bizerr.ErrCodeInvalidNodeQuantity, "invalid tikv node quantity"))
				}

				if c.NodeProfileName == "" {
					types.BizErrorRequestResp(ctx, bizerr.New(bizerr.ErrCodeInvalidNodeSize, "invalid tikv node profile name"))
				}

				if c.StorageSizeGi <= 0 {
					types.BizErrorRequestResp(ctx, bizerr.New(bizerr.ErrCodeInvalidStorageSize, "invalid tikv storage size"))
				}

				if c.GetStorageType() != "" {
					types.BadRequestResp(ctx, fmt.Errorf("not support update storage type"))
					return
				}

				scaleReq.TikvNodeSetting = &openapidedicatedv1beta1.UpdateClusterRequest_StorageNodeSetting{
					NodeCount:     lo.ToPtr(c.Replicas),
					NodeSpecKey:   c.NodeProfileName,
					StorageSizeGi: c.StorageSizeGi,
				}

			case centralapi.ComponentType_COMPONENT_TYPE_TIFLASH:
				if c.Replicas < 0 {
					types.BizErrorRequestResp(ctx, bizerr.New(bizerr.ErrCodeInvalidNodeQuantity, "invalid tiflash node quantity"))
				}

				if c.StorageSizeGi < 0 {
					types.BizErrorRequestResp(ctx, bizerr.New(bizerr.ErrCodeInvalidStorageSize, "invalid tiflash storage size"))
				}

				if c.GetStorageType() != "" {
					types.BadRequestResp(ctx, fmt.Errorf("not support update storage type"))
					return
				}

				scaleReq.TiflashNodeSetting = &openapidedicatedv1beta1.UpdateClusterRequest_StorageNodeSetting{
					NodeCount:     lo.ToPtr(c.Replicas),
					NodeSpecKey:   c.NodeProfileName,
					StorageSizeGi: c.StorageSizeGi,
				}
			}
		}

		_, err := mgmtgrpc.UpdateDedicatedCluster(grpc.NewContext(ctx), &openapidedicatedv1beta1.UpdateClusterRequest{Cluster: scaleReq})
		if err != nil {
			types.GrpcErrorResp(ctx, err)
			return
		}
		types.SuccessResp(ctx, &centralapi.DefaultResp{
			BaseResp: &centralapi.BaseResp{
				ErrCode: int64(bizerr.ErrCodeOK),
				ErrMsg:  "",
			},
		})
		return
	}

	// core-portal
	resp, err := coregrpc.ScaleClusterV2(grpc.NewContext(ctx), req)
	if err != nil {
		types.GrpcErrorResp(ctx, err)
		return
	}
	types.SuccessResp(ctx, resp)
}
