package client

// @Time    : 2024/8/22 14:30
// @Author  : lvhongmeng

import (
	"context"
	"time"

	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/pingcap/errors"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
)

const (
	defaultQPS     = 500
	defaultBurst   = 1000
	defaultTimeout = 20 * time.Second
)

type K8sClient struct {
	mgr    manager.Manager
	Client client.Client // we can mock this Client for unit tests.
}

var K8sAPI = &K8sClient{}

func InitK8sClient(ctx context.Context, kubeConfigPath string) error {
	tilog.Info(ctx, "InitK8sClient:"+kubeConfigPath)
	var restCfg *rest.Config
	// Create Kubernetes config based on provided kubeConfigPath or InClusterConfig.
	restCfg, err := getKubeConfig(kubeConfigPath)
	if err != nil {
		tilog.Error(ctx, "getKubeConfig failed. ", zap.Error(err))
		return err
	}

	scheme := runtime.NewScheme()
	if err := cloudv1.AddToScheme(scheme); err != nil {
		return errors.Annotate(err, "register infra API cloudv1")
	}
	if err := corev1.AddToScheme(scheme); err != nil {
		return errors.Annotate(err, "register infra API corev1")
	}
	if err := v1alpha1.AddToScheme(scheme); err != nil {
		return errors.Annotate(err, "register infra API corev1")
	}

	mgr, err := ctrl.NewManager(
		restCfg, ctrl.Options{
			Scheme:         scheme,
			LeaderElection: false,
			Metrics:        metricsserver.Options{BindAddress: "0"},
		},
	)
	if err != nil {
		return errors.Annotate(err, "create controller-runtime manager failed")
	}

	// Start the manager in a separate goroutine.
	go func() {
		err := mgr.Start(context.TODO())
		if err != nil {
			panic("failed to start mgr: " + err.Error())
		}
	}()
	K8sAPI = &K8sClient{
		mgr:    mgr,
		Client: mgr.GetClient(),
	}
	return nil
}

func getKubeConfig(kubeConfigPath string) (*rest.Config, error) {
	var conf *rest.Config
	var err error
	if kubeConfigPath != "" {
		conf, err = clientcmd.BuildConfigFromFlags("", kubeConfigPath)
	} else {
		conf, err = rest.InClusterConfig()
	}
	if err != nil {
		return nil, errors.Annotate(err, "initialize the kubernetes config failed")
	}

	// Overwrite some configurations.
	conf.QPS = defaultQPS
	conf.Burst = defaultBurst
	conf.Timeout = defaultTimeout

	return conf, nil
}
