package commonazure

import (
	"context"
	"fmt"
	"sort"
	"strings"

	corev1 "k8s.io/api/core/v1"
	storagev1 "k8s.io/api/storage/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"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"
)

var (
	storageClassBindingMode   = storagev1.VolumeBindingWaitForFirstConsumer
	storageClassReclaimPolicy = corev1.PersistentVolumeReclaimDelete
)

// StorageClassConfig holds the agent-specific fields needed by StorageClassTask.
type StorageClassConfig struct {
	// Tags returns the billing/metadata tags to embed in the StorageClass parameters.
	Tags func() map[string]string

	// ClusterMetaFunc optionally provides a ClusterMeta override.
	// If nil, the default empty ClusterMeta from BaseTask is used.
	ClusterMetaFunc func() deploy.ClusterMeta
}

// StorageClassTask creates an Azure Managed Disk StorageClass on the remote cluster.
//
// This shared implementation eliminates code duplication between o11yk8sagent,
// o11ytidbagent, and kubernetesagent.
type StorageClassTask[T client.Object] struct {
	*reconciler.BaseTask[T]
	cfg StorageClassConfig
}

func NewStorageClassTask[T client.Object](
	ctx *reconciler.Context[T],
	cfg StorageClassConfig,
) *StorageClassTask[T] {
	return &StorageClassTask[T]{
		BaseTask: &reconciler.BaseTask[T]{Context: ctx},
		cfg:      cfg,
	}
}

func (t *StorageClassTask[T]) Name() string {
	return "StorageClass"
}

func (t *StorageClassTask[T]) ClusterMeta() deploy.ClusterMeta {
	if t.cfg.ClusterMetaFunc != nil {
		return t.cfg.ClusterMetaFunc()
	}
	return deploy.ClusterMeta{}
}

func (t *StorageClassTask[T]) Create(ctx context.Context) deploy.Result {
	tagsMap := t.cfg.Tags()
	tagSlice := make([]string, 0, len(tagsMap))
	for k, v := range tagsMap {
		tagSlice = append(tagSlice, fmt.Sprintf("%s=%s", k, v))
	}
	sort.Strings(tagSlice)

	sc := &storagev1.StorageClass{
		ObjectMeta: metav1.ObjectMeta{
			Name: consts.MetricsAgentStorageClassName,
		},
		Parameters: map[string]string{
			"skuName":     consts.AzureVolumeTypePremiumLRS,
			"cachingmode": "ReadOnly",
			"fsType":      "ext4",
			"tags":        strings.Join(tagSlice, ","),
		},
		VolumeBindingMode:    &storageClassBindingMode,
		AllowVolumeExpansion: ptr.To(true),
		ReclaimPolicy:        &storageClassReclaimPolicy,
		Provisioner:          "disk.csi.azure.com",
	}

	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	_, err = controllerutil.CreateOrUpdate(ctx, cli, sc, func() error { return nil })
	return deploy.PanicOrDone(err)
}
