package k8sresource

import (
	"fmt"
	"strings"

	regionalpb "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	bizerr "github.com/tidbcloud/infra-provider/regional-server/pkg/common/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

const scaleStatusConfigMapNamePrefix = "tidb-cluster-scale-status-"

// QueryCheckInput is passed to every registered QueryCheckFunc.
type QueryCheckInput struct {
	Req        *regionalpb.ListK8SResourcesRequest
	GVK        schema.GroupVersionKind
	Namespaced bool
}

// QueryCheckFunc validates list/get query conditions for a registered GK.
type QueryCheckFunc func(in QueryCheckInput) error

// CheckQueryPolicy runs the registered checker for resolved G+K, if any.
// Unlisted GK passes through without resolving scope.
func CheckQueryPolicy(mapper meta.RESTMapper, gvk schema.GroupVersionKind, req *regionalpb.ListK8SResourcesRequest) error {
	checks, ok := gkQueryChecks[gvk.Group]
	if !ok {
		return nil
	}
	fn, ok := checks[gvk.Kind]
	if !ok {
		return nil
	}
	namespaced, err := resolveNamespaced(mapper, gvk)
	if err != nil {
		return err
	}
	return fn(QueryCheckInput{Req: req, GVK: gvk, Namespaced: namespaced})
}

func requireSelectorNameAndNamespace(sel *regionalpb.Selector) (namespace, name string, err error) {
	if sel == nil {
		return "", "", bizerr.New(bizerr.ParameterInvalid, "selector is required")
	}
	namespace = strings.TrimSpace(sel.Namespace)
	if namespace == "" {
		return "", "", bizerr.New(bizerr.ParameterInvalid, "selector.namespace is required")
	}
	name = strings.TrimSpace(sel.Name)
	if name == "" {
		return "", "", bizerr.New(bizerr.ParameterInvalid, "selector.name is required")
	}
	return namespace, name, nil
}

func requireNamePrefix(name, prefix string) error {
	if !strings.HasPrefix(name, prefix) {
		return bizerr.New(bizerr.ParameterInvalid,
			fmt.Sprintf("selector.name must have prefix %q", prefix))
	}
	return nil
}

func checkConfigMapQuery(in QueryCheckInput) error {
	if in.Req.GetPlaneType() == regionalpb.PlaneType_DATA_PLANE {
		return bizerr.New(bizerr.ParameterInvalid, "ConfigMap is not allowed on DATA_PLANE")
	}
	_, name, err := requireSelectorNameAndNamespace(in.Req.GetSelector())
	if err != nil {
		return err
	}
	return requireNamePrefix(name, scaleStatusConfigMapNamePrefix)
}

// gkQueryChecks holds per-GK query condition restrictions. Unlisted GK is unrestricted.
var gkQueryChecks = map[string]map[string]QueryCheckFunc{
	"": {
		"ConfigMap": checkConfigMapQuery,
	},
}
