package commonazure

import (
	"context"
	"testing"

	"github.com/pingcap/errors"
	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	storagev1 "k8s.io/api/storage/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	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/k8sclient/k8scliv2"
	"github.com/tidbcloud/observability/agent-control-server/pkg/reconciler"
	"github.com/tidbcloud/observability/agent-control-server/pkg/testkits"
)

func TestStorageClassTask_Create(t *testing.T) {
	tests := []struct {
		name           string
		agent          *agentv2.KubernetesAgent
		wrapCli        func(client.Client) client.Client
		setupResources func(client.Client)
		verify         func(t *testing.T, c client.Client)
		errMsg         string
	}{
		{
			name: "success create storage class",
			agent: &agentv2.KubernetesAgent{
				ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "agent"},
				Spec: agentv2.KubernetesAgentSpec{
					KubernetesClusterName: "cluster-1",
					O11y: agentv2.O11yConfigV2{
						TenantID: "t-123",
					},
					AKS: &agentv2.AKSConfig{},
				},
			},
			wrapCli: func(c client.Client) client.Client {
				return c
			},
			verify: func(t *testing.T, c client.Client) {
				var sc storagev1.StorageClass
				err := c.Get(context.Background(), client.ObjectKey{Name: consts.MetricsAgentStorageClassName}, &sc)
				assert.NoError(t, err)

				assert.Equal(t, consts.AzureVolumeTypePremiumLRS, sc.Parameters["skuName"])
				assert.Equal(t, "ReadOnly", sc.Parameters["cachingmode"])
				assert.Equal(t, "ext4", sc.Parameters["fsType"])
				assert.Contains(t, sc.Parameters["tags"], "servicetype=o11y")
				assert.Contains(t, sc.Parameters["tags"], "tenant=t-123")
				assert.Contains(t, sc.Parameters["tags"], "usedby=o11y-agent")
				assert.Contains(t, sc.Parameters["tags"], "Vendor=PingCAP")

				assert.True(t, *sc.AllowVolumeExpansion)
				assert.Equal(t, storagev1.VolumeBindingWaitForFirstConsumer, *sc.VolumeBindingMode)
				assert.Equal(t, corev1.PersistentVolumeReclaimDelete, *sc.ReclaimPolicy)
				assert.Equal(t, "disk.csi.azure.com", sc.Provisioner)
			},
		},
		{
			name: "failed to get client",
			agent: &agentv2.KubernetesAgent{
				ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "agent"},
				Spec: agentv2.KubernetesAgentSpec{
					KubernetesClusterName: "cluster-1",
					O11y: agentv2.O11yConfigV2{
						TenantID: "t-123",
					},
					AKS: &agentv2.AKSConfig{},
				},
			},
			wrapCli: func(cli client.Client) client.Client {
				return testkits.NewClientGetter(cli).WithCreateFunc(func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
					return errors.New("failed to create client")
				})
			},
			errMsg: "failed to create client",
		},
		{
			name: "failed to create storage class",
			agent: &agentv2.KubernetesAgent{
				ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "agent"},
				Spec: agentv2.KubernetesAgentSpec{
					KubernetesClusterName: "cluster-1",
					O11y: agentv2.O11yConfigV2{
						TenantID: "t-123",
					},
					AKS: &agentv2.AKSConfig{},
				},
			},
			wrapCli: func(cli client.Client) client.Client {
				return testkits.NewClientGetter(cli).WithCreateFunc(func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
					return errors.New("failed to create storage class")
				})
			},
			errMsg: "failed to create storage class",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient()

			cli := tt.wrapCli(fakeCli)
			if tt.setupResources != nil {
				tt.setupResources(cli)
			}

			rctx := &reconciler.Context[*agentv2.KubernetesAgent]{
				Agent: tt.agent,
				Local: cli,
			}
			rctx.Remote = k8scliv2.NewCacheClientGetter(testkits.NewClientGetter(cli))

			task := NewStorageClassTask(rctx, StorageClassConfig{
				Tags: func() map[string]string {
					return map[string]string{
						consts.TagKeyBillingServiceType: "o11y",
						consts.TagKeyBillingTenant:      tt.agent.Spec.O11y.TenantID,
						consts.TagKeyBillingUsedBy:      "o11y-agent",
						consts.CrossplaneKeyVendor:      consts.CrossplaneValueVendor,
					}
				},
				ClusterMetaFunc: func() deploy.ClusterMeta {
					return deploy.ClusterMeta{
						K8sClusterName: tt.agent.Spec.KubernetesClusterName,
					}
				},
			})
			assert.Equal(t, "StorageClass", task.Name())
			assert.Equal(t, "cluster-1", task.ClusterMeta().K8sClusterName)

			res := task.Create(context.Background())
			if tt.errMsg != "" {
				assert.Contains(t, res.Error().Error(), tt.errMsg)
			} else {
				assert.Equal(t, deploy.Done(), res)
			}

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