package main

import (
	"context"
	"fmt"
	"log"
	"sync/atomic"
	"time"

	"github.com/tidbcloud/aws-shared-provider/pkg/utils/random"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

/*
GOOS=linux GOARCH=arm64 go build -v -o eks-qps-arm64
docker buildx build --platform=linux/arm64 --push -t eks-qps:multi-client .

cat <<EOF | kubectl apply -n kube-system -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: eks-qps
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: eks-qps
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: eks-qps
subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system
EOF

k run -n kube-system --image eks-qps:multi-client --image-pull-policy=Always eks-qps
// k run -n kube-system --image eks-qps:multi-client --image-pull-policy=Always eks-qps-1
// you can run multi processes, because its PATCH operation.
*/

var total int64
var config *rest.Config

func benchmark(label string) {
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatal("NewForConfig", err)
	}
	ctx := context.TODO()
	podListSys, err := clientset.CoreV1().Pods("kube-system").List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("app=%s", label),
	})
	if err != nil {
		log.Fatal("podListSys", err)
	}
	if len(podListSys.Items) == 0 {
		log.Fatalf("no pod app=%s in kube-system namespace", label)
	}

	log.Printf("-------- start eks-qps patch %s, try %d concurrency --------", label, len(podListSys.Items))

	for _, po := range podListSys.Items {
		po, err := clientset.CoreV1().Pods("kube-system").Get(ctx, po.Name, metav1.GetOptions{})
		if err != nil {
			log.Fatalf("get pod %v err: %v", po.Name, err)
		}
		go func(po *corev1.Pod) {
			for {
				//nolint: gomnd // not need
				// po.Annotations[fmt.Sprintf("tidbcloud.com/%s", label)] = random.Random(4)
				// po, err = clientset.CoreV1().Pods("kube-system").Update(ctx, po, metav1.UpdateOptions{})
				// if err != nil {
				// 	log.Fatalf("update pod %v err: %v", po.Name, err)
				// }
				//nolint: gomnd // not need
				data := fmt.Sprintf(`{"metadata":{"annotations":{"tidbcloud.com/%s":"%s"}}}`, label, random.Random(4))
				_, err = clientset.CoreV1().Pods("kube-system").Patch(
					ctx, po.Name, types.MergePatchType, []byte(data), metav1.PatchOptions{})
				if err != nil {
					log.Fatalf("patch pod %v err: %v", po.Name, err)
				}
				atomic.AddInt64(&total, 1)
			}
		}(po)
	}
}

func main() {
	var err error
	config, err = rest.InClusterConfig()
	if err != nil {
		log.Fatal("InClusterConfig", err)
	}
	config.QPS = 4000
	config.Burst = 4000

	// run multi kube clientset
	benchmark("whl") // run command first: k create deployment -n kube-system --image nginx:alpine --replicas 10 whl
	benchmark("aaa") // run command first: k create deployment -n kube-system --image nginx:alpine --replicas 10 aaa
	benchmark("bbb") // run command first: k create deployment -n kube-system --image nginx:alpine --replicas 10 bbb
	benchmark("ccc") // run command first: k create deployment -n kube-system --image nginx:alpine --replicas 10 ccc
	benchmark("ddd") // run command first: k create deployment -n kube-system --image nginx:alpine --replicas 10 ddd
	benchmark("eee") // run command first: k create deployment -n kube-system --image nginx:alpine --replicas 10 eee

	//nolint: gomnd // not need
	ticker := time.NewTicker(10 * time.Second)
	defer ticker.Stop()
	preTotal := int64(0)
	for {
		<-ticker.C
		nowTotal := atomic.LoadInt64(&total)
		//nolint: gomnd // not need
		log.Printf("eks all qps: %.2f", float64(nowTotal-preTotal)/float64(10))
		preTotal = nowTotal
	}
}
