package api

import (
	"context"
	"fmt"
	"net/http"
	"sort"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/errcode"
	"github.com/tidbcloud/core-portal/internal/admin/forms"
	"github.com/tidbcloud/core-portal/internal/central/config"
	"github.com/tidbcloud/core-portal/internal/central/grpc/grpcli"
	"github.com/tidbcloud/core-svc/idl/pbgen/proto/core"
	entity "github.com/tidbcloud/core-svc/model"
	"github.com/tidbcloud/pkgv2/bizerr"
)

func checkProfileManageApiKey(apiKeyEntity *entity.APIKey) error {
	if apiKeyEntity.Name != config.ProfileMngApiKeyName || apiKeyEntity.TenantID != entity.PingCAPTenantID {
		return errcode.NewNotAuthenticatedErr(fmt.Errorf("invalid api key"))
	}
	return nil
}

// @Summary CalcNodeProfiles
// @Tags Profile
// @Description CalcNodeProfiles
// @Accept  multipart/form-data
// @Produce json
// @Param files formData forms.CalcNodeProfileReq true "calc node profile"
// @Success 200 {object} forms.CalcNodeProfileResp
// @Router /api/v1/admin/profile/query [post]
func CalcNodeProfiles(c *gin.Context) {
	ctx := c.Request.Context()
	statusCode := http.StatusOK
	resp := &forms.CalcNodeProfileResp{}
	req := &forms.CalcNodeProfileReq{}

	if err := c.ShouldBindJSON(req); err != nil {
		resp.Code = int64(bizerr.ErrCodeParams)
		resp.Msg = fmt.Sprintf("invalid param: %s", err)
		c.JSON(statusCode, resp)
		return
	}

	profiles, err := calcNodeProfiles(ctx, req)
	if err != nil {
		err = err.Redact()
		resp.Code = int64(err.ErrCode())
		resp.Msg = err.ErrMsg()
		statusCode = err.HttpStatus()
	} else {
		resp.ComponentNodeProfiles = profiles
	}
	c.JSON(statusCode, resp)
}

func calcNodeProfiles(ctx context.Context, req *forms.CalcNodeProfileReq) ([]*forms.NodeProfilesWithComponentType, bizerr.BizError) {
	listNodeProfiles4TenantReq := &core.ListNodeProfiles4TenantReq{
		TenantId:          req.OrgId,
		ProjectId:         req.ProjectId,
		CloudProviderName: req.Provider,
		RegionName:        req.Region,
		DeployType:        getClusterDeployType(req.IsDevTier),
	}
	resp, err := grpcli.ListNodeProfiles4Tenant(ctx, listNodeProfiles4TenantReq)
	if err != nil {
		return nil, err
	}

	nodeProfileMap := make(map[core.ComponentType][]*core.NodeProfile4Tenant)
	for _, nodeProfile := range resp.Profiles {
		nodeProfileMap[nodeProfile.ComponentType] = append(nodeProfileMap[nodeProfile.ComponentType], nodeProfile)
	}

	var nodeProfiles []*forms.NodeProfilesWithComponentType
	for k, each := range nodeProfileMap {
		profiles := make([]*forms.NodeProfile, 0, len(each))
		for _, nodeProfile := range each {
			profiles = append(profiles, &forms.NodeProfile{
				Name:             nodeProfile.Name,
				DefaultNodeNum:   int(nodeProfile.DefaultNodeNum),
				MinStorageSizeGi: int(nodeProfile.MinStorageSizeGi),
				MaxStorageSizeGi: int(nodeProfile.MaxStorageSizeGi),
			})
		}
		sort.Slice(profiles, func(i, j int) bool {
			return profiles[i].Name < profiles[j].Name
		})

		nodeProfiles = append(nodeProfiles, &forms.NodeProfilesWithComponentType{
			ComponentType: k.String(),
			Profiles:      profiles, //fill later
		})
	}

	return nodeProfiles, nil
}

func getClusterDeployType(isDevTier bool) core.DeployType {
	if isDevTier {
		return core.DeployType_DEPLOY_TYPE_SHARED
	}
	return core.DeployType_DEPLOY_TYPE_DEDICATED
}
