package aws

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",
					},
					EKS: &agentv2.EKSConfig{},
				},
			},
			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, "ext4", sc.Parameters["csi.storage.k8s.io/fstype"])
				assert.Equal(t, consts.AWSVolumeTypeGP3, sc.Parameters["type"])
				assert.Equal(t, "true", sc.Parameters["encrypted"])
				assert.Equal(t, "3000", sc.Parameters["iops"])
				assert.Equal(t, "125", sc.Parameters["throughput"])
				assert.Equal(t, "Vendor=PingCAP", sc.Parameters["tagSpecification"])

				assert.Equal(t, "servicetype=o11y", sc.Parameters["tagSpecification_1"])
				assert.Equal(t, "tenant=t-123", sc.Parameters["tagSpecification_2"])
				assert.Equal(t, "usedby=o11y-agent", sc.Parameters["tagSpecification_3"])

				assert.True(t, *sc.AllowVolumeExpansion)
				assert.Equal(t, storagev1.VolumeBindingWaitForFirstConsumer, *sc.VolumeBindingMode)
				assert.Equal(t, corev1.PersistentVolumeReclaimDelete, *sc.ReclaimPolicy)
				assert.Equal(t, "ebs.csi.aws.com", sc.Provisioner)
				assert.Equal(t, []string{"nodelalloc,noatime"}, sc.MountOptions)
			},
		},
		{
			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",
					},
					EKS: &agentv2.EKSConfig{},
				},
			},
			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",
					},
					EKS: &agentv2.EKSConfig{},
				},
			},
			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)
			}

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

			task := NewStorageClassTask(ctx)
			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)
			}
		})
	}
}
