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"
)

// ResolveGVK resolves request GVK for the target cluster RESTMapper.
// Built-in kinds (CoreKind / WellKnownBuiltin) may omit group and version when consistent with the table.
// All other paths require gvk.group; gvk.version is optional.
//
// When version is omitted on the generic path, we use RESTMapper.RESTMapping (preferred version), not
// RESTMappings plus a strict len==1 check. See genericResolveGVK.
func ResolveGVK(mapper meta.RESTMapper, in *regionalpb.GVK) (schema.GroupVersionKind, error) {
	if mapper == nil {
		return schema.GroupVersionKind{}, bizerr.New(bizerr.K8SClientError, "RESTMapper is not available")
	}
	if in == nil {
		return schema.GroupVersionKind{}, bizerr.New(bizerr.ParameterInvalid, "gvk is required")
	}

	userGroup := strings.TrimSpace(in.Group)
	userVersion := strings.TrimSpace(in.Version)
	rawKind := strings.TrimSpace(in.Kind)
	if rawKind == "" {
		return schema.GroupVersionKind{}, bizerr.New(bizerr.ParameterInvalid, "gvk.kind is required")
	}

	if canonical, table, ok := lookupBuiltin(rawKind); ok {
		if gvkFieldsEmpty(userGroup, userVersion) {
			return mergeBuiltinGVK(canonical, "", "", table), nil
		}
		if gvkCompatible(userGroup, userVersion, table) {
			return mergeBuiltinGVK(canonical, userGroup, userVersion, table), nil
		}
		if userGroup == "" {
			return schema.GroupVersionKind{}, bizerr.New(bizerr.ParameterInvalid,
				fmt.Sprintf("gvk.group is required when kind %s conflicts with built-in %s/%s",
					canonical, table.group, table.version))
		}
	} else if userGroup == "" {
		return schema.GroupVersionKind{}, bizerr.New(bizerr.ParameterInvalid, "gvk.group is required")
	}
	// Generic path uses rawKind (not canonical) so incompatible built-in spellings can target CRs.
	return genericResolveGVK(mapper, userGroup, userVersion, rawKind)
}

// genericResolveGVK resolves group+kind (+ optional version) via RESTMapper.
//
// Version resolution strategy (we keep the left-hand choice):
//   - RESTMapping(gk[, version]): Discovery preferred version when version is empty; aligns with
//     kubectl-style defaults. Success does not mean only one version exists on the cluster.
//   - RESTMappings + require len==1: enumerate all served versions and reject unless unique; stricter
//     for APIs but forces callers to pass version less often only when preferred is unambiguous.
//
// We use RESTMapping so missing gvk.version is usable; mapper AmbiguousKindError is surfaced as 400
// asking for an explicit version. Switch to RESTMappings if product policy requires strict uniqueness.
func genericResolveGVK(mapper meta.RESTMapper, group, version, kind string) (schema.GroupVersionKind, error) {
	gk := schema.GroupKind{Group: group, Kind: kind}
	var (
		mapping *meta.RESTMapping
		err     error
	)
	if version != "" {
		mapping, err = mapper.RESTMapping(gk, version)
	} else {
		mapping, err = mapper.RESTMapping(gk) // preferred version; see comment above
	}
	if err != nil {
		return schema.GroupVersionKind{}, mapRESTMapperError(err, group, kind)
	}
	return mapping.GroupVersionKind, nil
}
