package utils

import (
	"context"
	"slices"

	"github.com/juju/errors"
	log "github.com/sirupsen/logrus"
	ext_clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/rest"
)

func CheckResources(crdNameOrCategories []string, config *rest.Config) (bool, error) {
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		return false, errors.Annotatef(err, "Failed to create dynamic client")
	}

	clientset, err := ext_clientset.NewForConfig(config)
	if err != nil {
		return false, errors.Annotatef(err, "Failed to create apiextensions client")
	}
	gvrs, err := findGVRs(clientset, crdNameOrCategories)
	if err != nil {
		return false, err
	}

	for _, gvr := range gvrs {
		hasResources, err := checkCRDResources(dynamicClient, gvr)
		if err != nil {
			return false, err
		}
		if hasResources {
			log.Infof("Found resources of %s.%s/%s", gvr.Resource, gvr.Group, gvr.Version)
			return true, nil
		}
	}
	return false, nil
}

// 根据 CRD 名称查询所有 CRD
func findGVRs(client *ext_clientset.Clientset, crdNameOrCategories []string) ([]schema.GroupVersionResource, error) {
	crdList, err := client.ApiextensionsV1().CustomResourceDefinitions().List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return nil, errors.Annotate(err, "Failed to list all CRDs")
	}

	gvrs := []schema.GroupVersionResource{}
	for _, crd := range crdList.Items {
		if slices.Contains(crdNameOrCategories, crd.Name) || // match full name
			slices.Contains(crdNameOrCategories, crd.Spec.Names.Singular) || // match singular name
			slices.Contains(crdNameOrCategories, crd.Spec.Names.Plural) || // match plural name
			slices.ContainsFunc(crd.Spec.Names.Categories, func(category string) bool { return slices.Contains(crdNameOrCategories, category) }) {
			for _, v := range crd.Spec.Versions {
				gvr := schema.GroupVersionResource{
					Group:    crd.Spec.Group,
					Version:  v.Name,
					Resource: crd.Spec.Names.Plural,
				}
				gvrs = append(gvrs, gvr)
			}
		}
	}
	return gvrs, nil
}

func checkCRDResources(dynamicClient dynamic.Interface, gvr schema.GroupVersionResource) (bool, error) {
	list, err := dynamicClient.Resource(gvr).Namespace("").List(context.TODO(), metav1.ListOptions{
		Limit: 1,
	})
	if err != nil {
		if !k8serrors.IsNotFound(err) {
			return false, errors.Annotatef(err, "Failed to list resources of %s.%s/%s", gvr.Resource, gvr.Group, gvr.Version)
		} else {
			return false, nil
		}
	}

	return len(list.Items) > 0, nil
}
