package commonazure

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	aksv1beta1 "github.com/upbound/provider-azure/apis/containerservice/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/ptr"

	agentv2 "github.com/tidbcloud/observability/agent-control-server/api/v2"
	"github.com/tidbcloud/observability/agent-control-server/pkg/consts"
	"github.com/tidbcloud/observability/agent-control-server/pkg/deploy"
	"github.com/tidbcloud/observability/agent-control-server/pkg/reconciler"
	"github.com/tidbcloud/observability/agent-control-server/pkg/testkits"
	"github.com/tidbcloud/observability/agent-control-server/pkg/util"
)

func makeKubeletIdentityAgent() *agentv2.KubernetesAgent {
	return &agentv2.KubernetesAgent{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-agent",
			Namespace: "default",
		},
	}
}

func TestKubeletIdentityTask_BasicMeta(t *testing.T) {
	cli := testkits.BuildClient(makeKubeletIdentityAgent())
	rctx := &reconciler.Context[*agentv2.KubernetesAgent]{
		Agent: makeKubeletIdentityAgent(),
		Local: cli,
	}
	cm := deploy.ClusterMeta{K8sClusterName: "cluster-1"}
	task := NewKubeletIdentityTask(rctx, cm)

	assert.Equal(t, "KubeletIdentity", task.Name())
	assert.Equal(t, cm, task.ClusterMeta())

	// Delete is a no-op.
	assert.Equal(t, deploy.Done(), task.Delete(context.Background()))
}

func TestKubeletIdentityTask_Create(t *testing.T) {
	tests := []struct {
		name        string
		mutateAgent func(*agentv2.KubernetesAgent)
		setupAKS    func() *aksv1beta1.KubernetesCluster
		clusterName string
		errMsg      string
		verify      func(*testing.T, *agentv2.KubernetesAgent)
	}{
		{
			name: "skip if already fetched",
			mutateAgent: func(a *agentv2.KubernetesAgent) {
				util.SetAnnotation(a, consts.AnnotationKeyAKSKubeletClientID, "existing-client")
				util.SetAnnotation(a, consts.AnnotationKeyAKSKubeletObjectID, "existing-object")
			},
			clusterName: "cluster-1",
			verify: func(t *testing.T, a *agentv2.KubernetesAgent) {
				assert.Equal(t, "existing-client", util.GetAnnotation(a, consts.AnnotationKeyAKSKubeletClientID))
				assert.Equal(t, "existing-object", util.GetAnnotation(a, consts.AnnotationKeyAKSKubeletObjectID))
			},
		},
		{
			name:        "empty cluster name",
			clusterName: "",
			errMsg:      "kubernetes cluster name is empty",
		},
		{
			name:        "cluster not found",
			clusterName: "missing-cluster",
			errMsg:      "failed to get KubernetesCluster",
		},
		{
			name:        "kubelet identity not available",
			clusterName: "cluster-1",
			setupAKS: func() *aksv1beta1.KubernetesCluster {
				return &aksv1beta1.KubernetesCluster{
					ObjectMeta: metav1.ObjectMeta{Name: "cluster-1"},
				}
			},
			errMsg: "kubeletIdentity not available yet",
		},
		{
			name:        "success",
			clusterName: "cluster-1",
			setupAKS: func() *aksv1beta1.KubernetesCluster {
				aks := &aksv1beta1.KubernetesCluster{
					ObjectMeta: metav1.ObjectMeta{Name: "cluster-1"},
				}
				aks.Status.AtProvider.KubeletIdentity = []aksv1beta1.KubeletIdentityObservation{
					{ClientID: ptr.To("client-id-1"), ObjectID: ptr.To("object-id-1")},
				}
				return aks
			},
			verify: func(t *testing.T, a *agentv2.KubernetesAgent) {
				assert.Equal(t, "client-id-1", util.GetAnnotation(a, consts.AnnotationKeyAKSKubeletClientID))
				assert.Equal(t, "object-id-1", util.GetAnnotation(a, consts.AnnotationKeyAKSKubeletObjectID))
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			agent := makeKubeletIdentityAgent()
			if tt.mutateAgent != nil {
				tt.mutateAgent(agent)
			}

			cli := testkits.BuildClient(agent)
			if tt.setupAKS != nil {
				assert.NoError(t, cli.Create(context.Background(), tt.setupAKS()))
			}

			rctx := &reconciler.Context[*agentv2.KubernetesAgent]{
				Agent: agent,
				Local: cli,
			}
			task := NewKubeletIdentityTask(rctx, deploy.ClusterMeta{K8sClusterName: tt.clusterName})
			res := task.Create(context.Background())

			if tt.errMsg != "" {
				assert.Error(t, res.Error())
				assert.Contains(t, res.Error().Error(), tt.errMsg)
			} else {
				assert.NoError(t, res.Error())
				assert.Equal(t, deploy.Done(), res)
			}

			if tt.verify != nil {
				tt.verify(t, agent)
			}
		})
	}
}
