// Package converter is responsible for converting the types registered with a k8s server to schemas which can be used
// by the UI (and other consumers) to discover the resources available and the current user's permissions.
package converter

import (
	"fmt"
	"strings"

	"github.com/rancher/apiserver/pkg/types"
	"github.com/rancher/norman/types/convert"
	v1 "github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/discovery"
	"k8s.io/kube-openapi/pkg/util/proto"
)

const (
	gvkExtensionName    = "x-kubernetes-group-version-kind"
	gvkExtensionGroup   = "group"
	gvkExtensionVersion = "version"
	gvkExtensionKind    = "kind"
)

func GVKToVersionedSchemaID(gvk schema.GroupVersionKind) string {
	if gvk.Group == "" {
		return strings.ToLower(fmt.Sprintf("core.%s.%s", gvk.Version, gvk.Kind))
	}
	return strings.ToLower(fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Kind))
}

func gvrToPluralName(gvr schema.GroupVersionResource) string {
	if gvr.Group == "" {
		return fmt.Sprintf("core.%s.%s", gvr.Version, gvr.Resource)
	}
	return fmt.Sprintf("%s.%s.%s", gvr.Group, gvr.Version, gvr.Resource)
}

func GVKToSchemaID(gvk schema.GroupVersionKind) string {
	if gvk.Group == "" {
		return strings.ToLower(gvk.Kind)
	}
	return strings.ToLower(fmt.Sprintf("%s.%s", gvk.Group, gvk.Kind))
}

func GVRToPluralName(gvr schema.GroupVersionResource) string {
	if gvr.Group == "" {
		return gvr.Resource
	}
	return fmt.Sprintf("%s.%s", gvr.Group, gvr.Resource)
}

// GetGVKForKind attempts to retrieve a GVK for a given Kind. Not all kind represent top level resources,
// so this function may return nil if the kind did not have a gvk extension
func GetGVKForKind(kind *proto.Kind) *schema.GroupVersionKind {
	extensions, ok := kind.Extensions[gvkExtensionName].([]any)
	if !ok {
		return nil
	}
	for _, extension := range extensions {
		if gvkExtension, ok := extension.(map[any]any); ok {
			gvk := schema.GroupVersionKind{
				Group:   convert.ToString(gvkExtension[gvkExtensionGroup]),
				Version: convert.ToString(gvkExtension[gvkExtensionVersion]),
				Kind:    convert.ToString(gvkExtension[gvkExtensionKind]),
			}
			return &gvk
		}
	}
	return nil
}

// ToSchemas creates the schemas for a K8s server, using client to discover groups/resources, and crd to potentially
// add additional information about new fields/resources. Mostly ties together addDiscovery and addCustomResources.
func ToSchemas(crd v1.CustomResourceDefinitionClient, client discovery.DiscoveryInterface) (map[string]*types.APISchema, error) {
	result := map[string]*types.APISchema{}

	if err := addDiscovery(client, result); err != nil {
		return nil, err
	}

	if err := addCustomResources(crd, result); err != nil {
		return nil, err
	}

	if err := addDescription(client, result); err != nil {
		return nil, err
	}

	return result, nil
}