package globalapi

import (
	"context"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/message"
	"github.com/tidbcloud/observability/pkg/common/structs"
	globalstructs "github.com/tidbcloud/observability/pkg/globalservice/structs"
	serverlessglobalv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	"go.uber.org/zap"
)

func enrichShowDeletedQueryClusters(c *gin.Context) error {
	obj, ok := c.Get(structs.GlobalServerRequestParametersKey)
	if !ok {
		return nil
	}
	reqParams := obj.(structs.RequestParameters)

	query := message.QueryClustersReq{
		TenantID:      extractParam(reqParams, paramKeyTenantID),
		ProjectID:     extractParam(reqParams, paramKeyProjectID),
		ApplicationID: extractParam(reqParams, paramKeyApplicationID),
		BizType:       extractParam(reqParams, paramKeyBizType),
		ShowDeleted:   extractBoolParam(reqParams, "showDeleted"),
	}

	resolvedQuery, kubernetesID, err := resolveShowDeletedQueryProjectID(c.Request.Context(), query)
	if err != nil {
		log.Warn("resolve showDeleted projectID failed, falling back to original query",
			zap.Error(err),
			zap.String("applicationID", query.ApplicationID),
			zap.String("bizType", query.BizType))
		return nil
	}
	if resolvedQuery.ProjectID == "" || resolvedQuery.ProjectID == query.ProjectID {
		return nil
	}

	queryValues := c.Request.URL.Query()
	queryValues.Set(paramKeyProjectID, resolvedQuery.ProjectID)
	c.Request.URL.RawQuery = queryValues.Encode()

	if kubernetesID != "" {
		if reqParams.Headers == nil {
			reqParams.Headers = make(map[string]string)
		}
		reqParams.Headers[string(constants.OpenAPIHeaderKubernetesID)] = kubernetesID
		c.Set(structs.GlobalServerRequestParametersKey, reqParams)
		c.Request.Header.Set(string(constants.OpenAPIHeaderKubernetesID), kubernetesID)
	}
	return nil
}

func resolveShowDeletedQueryProjectID(ctx context.Context, query message.QueryClustersReq) (message.QueryClustersReq, string, error) {
	if !query.ShowDeleted || query.ProjectID != "" || !shouldResolveShowDeletedProjectID(query.BizType) {
		return query, "", nil
	}

	env, ok := envFromShowDeletedBizType(query.BizType)
	if !ok {
		return query, "", nil
	}

	clusterID, err := strconv.ParseUint(query.ApplicationID, 10, 64)
	if err != nil {
		return query, "", nil
	}

	resp, err := mgmtListClustersFunc(ctx, env, clusterID, true)
	if err != nil {
		return query, "", err
	}
	clusters := resp.GetClusters()
	if len(clusters) == 0 {
		return query, "", nil
	}

	cluster := clusters[0]
	originalProjectID := cluster.GetOriginalProjectId()
	if originalProjectID == 0 {
		return query, "", nil
	}

	query.ProjectID = strconv.FormatUint(originalProjectID, 10)
	return query, extractServerlessKubernetesID(query, cluster.GetServerlessCluster()), nil
}

func extractServerlessKubernetesID(query message.QueryClustersReq, cluster *serverlessglobalv1.ServerlessServiceCluster) string {
	if !shouldResolveServerlessChild(globalstructs.RoutingContext{
		BizType:       query.BizType,
		ApplicationID: query.ApplicationID,
	}) || cluster == nil || cluster.GetCluster() == nil {
		return ""
	}
	return cluster.GetCluster().GetKubernetes()
}

func shouldResolveShowDeletedProjectID(bizType string) bool {
	return bizType == "tidbcloud" ||
		strings.HasSuffix(bizType, "-tidbcloud") ||
		strings.HasSuffix(bizType, "-devtier")
}

func envFromShowDeletedBizType(bizType string) (string, bool) {
	switch {
	case bizType == "tidbcloud":
		return constants.EnvProd, true
	case strings.HasPrefix(bizType, constants.EnvDev+"-"):
		return constants.EnvDev, true
	case strings.HasPrefix(bizType, constants.EnvStaging+"-"):
		return constants.EnvStaging, true
	case strings.HasPrefix(bizType, constants.EnvProd+"-"):
		return constants.EnvProd, true
	default:
		return "", false
	}
}
