package managerserver

import (
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/pingcap/errors"
	pkginfra "github.com/tidbcloud/aws-shared-provider/pkg/infra"
	"github.com/tidbcloud/aws-shared-provider/pkg/metrics"
	"github.com/tidbcloud/aws-shared-provider/pkg/types"
	infra "github.com/tidbcloud/tidb-management-service/api/serverless_infra"
)

func (s *server) resourceGroup(w http.ResponseWriter, r *http.Request) {
	logger := log.WithName("tidb/resource_group")
	logger.Info("req param", "req", r.URL)

	var err error
	start := time.Now()

	defer func() {
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			_, _ = io.WriteString(w, err.Error())
			metrics.RequestHistogram.WithLabelValues("resource_group", "500", "").Observe(time.Since(start).Seconds())
			return
		}
		metrics.RequestHistogram.WithLabelValues("resource_group", "200", "").Observe(time.Since(start).Seconds())
	}()

	token, user := r.URL.Query().Get("token"), r.URL.Query().Get("user")
	if token == "" {
		logger.Error(noTokenErr, "get token")
		err = noTokenErr
		return
	}
	if user == "" {
		err = errors.New("no user")
		logger.Error(err, "get user")
		return
	}
	logger = logger.WithValues("token", token, "user", user)

	regionalInfra := pkginfra.NewRegionalInfraWithClient(s.infraClusterCli, s.cfg.KubeClusterName)
	cluster, err := regionalInfra.GetClusterWithToken(r.Context(), token)
	if err != nil {
		logger.Error(err, "fail to get token")
		return
	}

	rgName, pool := r.URL.Query().Get("resource_group"), r.URL.Query().Get("pool")
	if strings.Contains(rgName, "/") {
		err = errors.New("invalid resource group")
		logger.Error(err, "resource_group", rgName)
		return
	}
	set := map[string]any{}
	var oldRgName string
	if rgName != "" {
		if old := cluster.Spec.TiDB.UserResourceGroup[user]; old != rgName {
			set[fmt.Sprintf("'spec.tidb'.user_resource_group.'%s'", user)] = rgName
			if old != "" {
				oldRgName = old
			}
		}
		if rg := cluster.Spec.TiDB.ResourceGroups[rgName]; rg != nil {
			if rg.Pool != pool {
				set[fmt.Sprintf("'spec.tidb'.resource_groups.'%s'.pool", rgName)] = pool
			}
		} else {
			if pool != "" && s.tManagers[pool] == nil {
				err = errors.Errorf("pool %s not found", pool)
				logger.Error(err, "resource_group", rgName, "pool", pool)
				return
			}
			set[fmt.Sprintf("'spec.tidb'.resource_groups.'%s'", rgName)] = &infra.ResourceGroup{
				Pool:     pool,
				Replicas: types.TidbResourceGroupMinReplicas,
			}
		}
	} else {
		if oldRgName = cluster.Spec.TiDB.UserResourceGroup[user]; oldRgName != "" {
			set[fmt.Sprintf("'spec.tidb'.user_resource_group.'%s'", user)] = nil
		}
	}
	if oldRgName != "" {
		var otherUseOldRg bool
		for u, sg := range cluster.Spec.TiDB.UserResourceGroup {
			if u != user && sg == oldRgName {
				otherUseOldRg = true
				break
			}
		}
		if !otherUseOldRg {
			set[fmt.Sprintf("'spec.tidb'.resource_groups.'%s'", oldRgName)] = nil
		}
	}

	if len(set) > 0 {
		if err = s.infraClusterCli.Patch(r.Context(), set, cluster); err != nil {
			logger.Error(err, "fail to set resource group", "set", set)
		}
	}
}
