package ngoperation

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/devops/pkg/infra/clients/coresvccli"
	"github.com/tidbcloud/devops/pkg/infra/clients/mgmthttpcli"
	commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
)

func toMgmtRegionID(provider, region string) commonv1.Region_Name {
	enumNameStr := strings.ReplaceAll(strings.ToUpper(fmt.Sprintf("%s_%s", provider, region)), "-", "_")
	return commonv1.Region_Name(commonv1.Region_Name_value[enumNameStr])
}

func GetResourcePoolAndSharedPool(ctx context.Context, id string, CoreSvc *coresvccli.CoreSvcCli, MgmtHTTP mgmthttpcli.MgmtHTTPClient) (*nextgenv1.ResourcePoolProfile, *mgmthttpcli.SharedPool, error) {
	rp, err := CoreSvc.Mgmt.GetResourcePoolString(ctx, id)
	if err != nil {
		return nil, nil, err
	}

	sp, err := MgmtHTTP.NextGenRegionServer(
		rp.GetRegion().GetLogicalName(),
		rp.GetRegion().GetCloudProviderId().String(),
	).GetSharedPool(ctx, id)
	if err != nil {
		return nil, nil, err
	}

	return rp, sp, nil
}

func GetResourcePoolAndSharedPoolWithRetry(ctx context.Context, id string, CoreSvc *coresvccli.CoreSvcCli, MgmtHTTP mgmthttpcli.MgmtHTTPClient, retries int) (*nextgenv1.ResourcePoolProfile, *mgmthttpcli.SharedPool, error) {
	var rp *nextgenv1.ResourcePoolProfile
	var sp *mgmthttpcli.SharedPool
	var err error
	for retries > 0 {
		rp, sp, err = GetResourcePoolAndSharedPool(ctx, id, CoreSvc, MgmtHTTP)
		if err == nil {
			return rp, sp, nil
		}
		retries--
		time.Sleep(time.Second * 10)
	}
	return nil, nil, err
}

func GetTiDBGroupWithRetry(ctx context.Context, cid string, groupID string, mgmtHTTP mgmthttpcli.MgmtHTTPClient, retries int) (*mgmthttpcli.TiDBGroup, error) {
	// sometimes GetCluster will return error, so retries added
	count := 3
	var tidbGroup *mgmthttpcli.TiDBGroup
	var err error
	for count > 0 {
		tidbGroup, err = mgmtHTTP.NextGen().GetTiDBGroup(ctx, cid, groupID)
		if err != nil {
			count--
			time.Sleep(time.Second * 10)
		} else {
			break
		}
	}
	if err != nil {
		return nil, err
	}
	return tidbGroup, nil
}

func GetLogicalRegionAndProviderString(region *commonv1.Region) (string, string) {
	return region.GetLogicalName(), region.GetCloudProviderId().String()
}

// computeNodeScaleDirection returns "up", "down", or "mixed" by comparing
// current vs desired node count and (optionally) node spec.
// A nil desiredSpec means spec is not being changed.
func computeNodeScaleDirection(currentCount, desiredCount uint32, currentSpec, desiredSpec *mgmthttpcli.NodeSpec) string {
	hasDown := desiredCount < currentCount
	hasUp := desiredCount > currentCount

	if desiredSpec != nil && currentSpec != nil {
		if desiredSpec.Cpu < currentSpec.Cpu || desiredSpec.MemoryGi < currentSpec.MemoryGi {
			hasDown = true
		}
		if desiredSpec.Cpu > currentSpec.Cpu || desiredSpec.MemoryGi > currentSpec.MemoryGi {
			hasUp = true
		}
	}

	if hasDown && hasUp {
		return "mixed"
	}
	if hasDown {
		return "down"
	}
	return "up"
}

// computeQuotaScaleDirection returns "up", "down", or "mixed" by comparing
// current vs desired max/min node count quota.
func computeQuotaScaleDirection(currentMax, currentMin, desiredMax, desiredMin uint32) string {
	hasDown := desiredMax < currentMax || desiredMin < currentMin
	hasUp := desiredMax > currentMax || desiredMin > currentMin

	if hasDown && hasUp {
		return "mixed"
	}
	if hasDown {
		return "down"
	}
	return "up"
}
