package task

import (
	"context"

	"github.com/tidbcloud/eks-provider/pkg/deploy"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	corev1 "k8s.io/api/core/v1"
	storagev1 "k8s.io/api/storage/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/pointer"
)

// Deried from github.com/tidbcloud/infra-api/controllers/provider/gardener/deploy.go
type StorageClass struct {
	*BaseTask
	classes []*storagev1.StorageClass
}

func gp2StorageClass() *storagev1.StorageClass {
	bindingMode := storagev1.VolumeBindingWaitForFirstConsumer
	reclaimPolicy := corev1.PersistentVolumeReclaimDelete
	return &storagev1.StorageClass{
		ObjectMeta: metav1.ObjectMeta{
			Name: "cloud-ssd",
		},
		VolumeBindingMode:    &bindingMode,
		AllowVolumeExpansion: pointer.Bool(true),
		ReclaimPolicy:        &reclaimPolicy,
		Provisioner:          "kubernetes.io/aws-ebs",
		Parameters: map[string]string{
			"type":   "gp2",
			"fsType": "ext4",
		},
	}
}

var storageClassProfiles = []struct {
	name       string
	iops       string
	throughput string
}{
	{
		name:       "tikv-gp3",
		iops:       "4000",
		throughput: "400",
	},
	{
		name:       "tiflash-gp3",
		iops:       "6000",
		throughput: "625",
	},
}

func gp3StorageClass(name, iops, throughput string) *storagev1.StorageClass {
	bindingMode := storagev1.VolumeBindingWaitForFirstConsumer
	reclaimPolicy := corev1.PersistentVolumeReclaimDelete
	return &storagev1.StorageClass{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		VolumeBindingMode:    &bindingMode,
		AllowVolumeExpansion: pointer.Bool(true),
		ReclaimPolicy:        &reclaimPolicy,
		Provisioner:          "ebs.csi.aws.com",
		Parameters: map[string]string{
			"csi.storage.k8s.io/fstype": "ext4",
			"type":                      "gp3",
			"iops":                      iops,
			"throughput":                throughput,
			"encrypted":                 "true",
		},
	}
}

func NewStorageClass(ctx *Context) *StorageClass {
	t := &StorageClass{BaseTask: &BaseTask{Context: ctx}}
	var classes = []*storagev1.StorageClass{gp2StorageClass()}
	for _, profile := range storageClassProfiles {
		classes = append(classes, gp3StorageClass(profile.name, profile.iops, profile.throughput))
	}
	t.classes = classes
	return t
}

func (t *StorageClass) Name() string {
	return "StorageClass"
}

func (t *StorageClass) Create(ctx context.Context) deploy.Result {
	if t.remote == nil {
		return deploy.Skip("empty remote client")
	}
	remote, err := t.remote.GetClient(ctx)
	if err != nil {
		return deploy.FromError(err)
	}
	for _, storageClass := range t.classes {
		_, err := controllerutil.CreateOrUpdate(ctx, remote, storageClass, func() error { return nil })
		if err != nil {
			return deploy.FromError(err)
		}
	}
	return deploy.FromError(nil)
}
