package infra

import (
	"context"
	"time"

	"github.com/tidbcloud/aws-shared-provider/pkg/scheme"
	labelsutil "github.com/tidbcloud/aws-shared-provider/pkg/utils/labels"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/rest"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/cache"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/cluster"
)

type LocalKubeImpl struct {
	kubeClient client.Client
	cluster    cluster.Cluster
}

func NewLocalKube(ctx context.Context, conf *rest.Config) (LocalKube, error) {
	selector, err := labels.Set{
		labelsutil.NameLabelKey:      labelsutil.TidbPodNameLabelValue,
		labelsutil.ComponentLabelKey: labelsutil.TidbPodComponentLabelValue,
	}.AsValidatedSelector()
	if err != nil {
		return nil, err
	}
	localCluster, err := cluster.New(conf, func(clusterOptions *cluster.Options) {
		clusterOptions.Scheme = scheme.Scheme
		clusterOptions.Cache = cache.Options{
			SyncPeriod: ptr.To[time.Duration](0),
			ByObject: map[client.Object]cache.ByObject{
				&corev1.Pod{}: {
					Label:                 selector,
					UnsafeDisableDeepCopy: ptr.To(true),
				},
			},
		}
	})
	if err != nil {
		return nil, err
	}

	if err = localCluster.GetFieldIndexer().IndexField(ctx, &corev1.Pod{},
		"status.podIP", func(obj client.Object) []string {
			pod := obj.(*corev1.Pod)
			if pod.Status.Phase == corev1.PodRunning {
				return []string{pod.Status.PodIP}
			}
			return nil
		}); err != nil {
		return nil, err
	}

	return &LocalKubeImpl{kubeClient: localCluster.GetClient(), cluster: localCluster}, nil
}

func (l *LocalKubeImpl) GetPodList(ctx context.Context, ip string) (*corev1.PodList, error) {
	podList := corev1.PodList{}
	err := l.kubeClient.List(ctx, &podList, client.MatchingFields{"status.podIP": ip})
	if err != nil {
		return nil, err
	}
	return &podList, nil
}

func (l *LocalKubeImpl) Start(ctx context.Context) error {
	return l.cluster.Start(ctx)
}

func (l *LocalKubeImpl) WaitForCacheSync(ctx context.Context) bool {
	return l.cluster.GetCache().WaitForCacheSync(ctx)
}
