package throttler

import (
	corev1 "k8s.io/api/core/v1"
	apitypes "k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/util/workqueue"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/event"

	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"github.com/tidbcloud/shared-provider/pkg/types"
)

func IsPodReady(p *corev1.Pod) bool {
	for i := range p.Status.Conditions {
		c := &p.Status.Conditions[i]
		if c.Type != corev1.PodReady {
			continue
		}
		if c.Status == corev1.ConditionTrue {
			return true
		}
	}

	return false
}

func (r *Reconciler) handleCreatePodEvent(e event.CreateEvent, q workqueue.RateLimitingInterface) {
	// nolint: forcetypeassert // obj will always be pod
	pod := e.Object.(*corev1.Pod)

	// skip unready pod sync
	if !IsPodReady(pod) {
		return
	}

	tcName, ok := pod.Labels[labelKeyInstance]
	if !ok {
		return
	}

	q.Add(ctrl.Request{
		NamespacedName: apitypes.NamespacedName{
			Namespace: pod.Namespace,
			Name:      tcName,
		},
	})
}

func (r *Reconciler) handleUpdatePodEvent(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
	// nolint: forcetypeassert // obj will always be pod
	podNew := e.ObjectNew.(*corev1.Pod)

	// skip unready pod sync
	if !IsPodReady(podNew) {
		return
	}

	tcName, ok := podNew.Labels[labelKeyInstance]
	if !ok {
		return
	}

	q.Add(ctrl.Request{
		NamespacedName: apitypes.NamespacedName{
			Namespace: podNew.Namespace,
			Name:      tcName,
		},
	})
}

func (r *Reconciler) handleCreateTidbClusterEvent(e event.CreateEvent, q workqueue.RateLimitingInterface) {
	// nolint: forcetypeassert // obj will always be tidbcluster
	tc := e.Object.(*v1alpha1.TidbCluster)

	q.Add(ctrl.Request{
		NamespacedName: apitypes.NamespacedName{
			Namespace: tc.Namespace,
			Name:      tc.Name,
		},
	})
}

func (r *Reconciler) handleUpdateTidbClusterEvent(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
	// nolint: forcetypeassert // obj will always be tidbcluster
	tcOld := e.ObjectOld.(*v1alpha1.TidbCluster)
	// nolint: forcetypeassert // obj will always be tidbcluster
	tcNew := e.ObjectNew.(*v1alpha1.TidbCluster)

	if !isThrottleAnnoChanged(tcOld.Annotations, tcNew.Annotations) {
		return
	}

	q.Add(ctrl.Request{
		NamespacedName: apitypes.NamespacedName{
			Namespace: tcNew.Namespace,
			Name:      tcNew.Name,
		},
	})
}

func isThrottleAnnoChanged(oldAnno, newAnno map[string]string) bool {
	keys := []string{
		types.AnnoKeyIngress,
		types.AnnoKeyEgress,
		types.AnnoKeyIngressPort,
		types.AnnoKeyEgressPort,
	}

	return isAnnoChanged(oldAnno, newAnno, keys)
}

func hasAnno(anno map[string]string, keys []string) bool {
	for _, key := range keys {
		_, ok := anno[key]
		if ok {
			return true
		}
	}
	return false
}

func isAnnoChanged(oldAnno, newAnno map[string]string, keys []string) bool {
	if oldAnno == nil && newAnno == nil {
		return false
	}
	if oldAnno == nil {
		return hasAnno(newAnno, keys)
	}
	if newAnno == nil {
		return hasAnno(oldAnno, keys)
	}
	for _, key := range keys {
		if oldAnno[key] != newAnno[key] {
			return true
		}
	}

	return false
}
