package job

import (
	"context"
	"os"
	"strings"

	"github.com/juju/errors"
	log "github.com/sirupsen/logrus"
	"github.com/tidbcloud/auto-deploy/internal/constants"
	"github.com/tidbcloud/auto-deploy/internal/utils"
	extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	applyv1 "k8s.io/apiextensions-apiserver/pkg/client/applyconfiguration/apiextensions/v1"
	ext_clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/yaml"
	"k8s.io/client-go/kubernetes/scheme"
)

func ApplyCRD(clientset *ext_clientset.Clientset, crdFile string) error {
	fd, err := os.Open(crdFile)
	if err != nil {
		log.WithField("file", crdFile).Error("Failed to open crd file")
		return errors.Annotatef(err, "Failed to open crd file: %s", crdFile)
	}
	decoder := yaml.NewYAMLOrJSONDecoder(fd, 1024)
	crd := applyv1.CustomResourceDefinitionApplyConfiguration{}
	err = decoder.Decode(&crd)
	if err != nil {
		log.WithField("file", crdFile).Error("Failed to decode crd")
		return errors.Annotatef(err, "Failed to decode crd: %s", crdFile)
	}
	versionNames := utils.Map(crd.Spec.Versions, func(v applyv1.CustomResourceDefinitionVersionApplyConfiguration) string { return *v.Name })
	log.WithFields(log.Fields{
		"file":     crdFile,
		"crd-name": *crd.Name,
		"group":    *crd.Spec.Group,
		"kind":     *crd.Spec.Names.Kind,
		"scope":    *crd.Spec.Scope,
		"versions": strings.Join(versionNames, ","),
	}).Debug("Load crd")

	// 注册 CRD 的 schema
	err = extv1.AddToScheme(scheme.Scheme)
	if err != nil {
		log.Error("Failed to decode crd")
		return errors.Annotatef(err, "Failed to registger crd schema")
	}
	log.Debug("Added crd schema")

	client := clientset.ApiextensionsV1().CustomResourceDefinitions()
	_, err = client.Apply(context.Background(), &crd, metav1.ApplyOptions{
		FieldManager: constants.K8S_APPLY_FIELD_MANAGER,
	})
	if err != nil {
		return errors.Annotatef(err, "Failed to convert secret yaml to json")
	}

	log.WithField("name", *crd.Name).Info("Apply CRD")
	return nil
}
