package tioperator

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/pingcap/tidb-operator/api/v2/core/v1alpha1"
	"github.com/samber/lo"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	schemav1 "github.com/tidbcloud/cluster-service-ng/pkg/storage/etcd/schema/v1"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

func ListTiFlashCNs(ctx context.Context, remoteCli client.Client, clusterId string) ([]*entity.Component, error) {
	tiflashGroups, err := listTiFlashGroups(ctx, remoteCli, dataplaneNamespace(clusterId))
	if err != nil {
		return nil, err
	}
	tiflashGroups = lo.Filter(tiflashGroups, func(comp *entity.Component, _ int) bool {
		return comp.GetComponentType() == apiv1.ComponentType_TIFLASH_COMPUTE
	})
	return tiflashGroups, nil
}

func GetTiFlashCN(ctx context.Context, remoteCli client.Client, clusterId, groupId string) (*entity.Component, error) {
	tiflashGroupCr, err := getTiFlashCNCR(ctx, remoteCli, dataplaneNamespace(clusterId), groupId)
	if err != nil {
		if apierrors.IsNotFound(err) {
			return nil, status.Errorf(codes.NotFound, "tiflash cn %s not found: %s", groupId, err)
		}
		return nil, status.Errorf(codes.Internal, "failed to get tiflash cn %s: %s", groupId, err)
	}

	tiflashComps, err := cvtTiFlashComponentToEn(ctx, tiflashGroupCr)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to convert tiflash cn %s: %s", groupId, err)
	}

	// tiflash have tiflash learner , so we need to filter it
	for _, tiflash := range tiflashComps {
		if tiflash.GetComponentType() == apiv1.ComponentType_TIFLASH_COMPUTE {
			return tiflash, nil
		}
	}

	// If no tiflash cn found, return not found error
	return nil, status.Errorf(codes.NotFound, "tiflash cn %s not found", groupId)
}

func UpdateTiFlashCN(ctx context.Context, remoteCli client.Client, clusterId, groupId string, options []UpdateOption) (*entity.Component, error) {
	tiflashGroupCr, err := getTiFlashCNCR(ctx, remoteCli, dataplaneNamespace(clusterId), groupId)
	if err != nil {
		if apierrors.IsNotFound(err) {
			return nil, status.Errorf(codes.NotFound, "tiflash cn %s not found: %s", groupId, err)
		}
		return nil, status.Errorf(codes.Internal, "failed to get tiflash cn %s: %s", groupId, err)
	}

	needUpdate, err := getUpdateExpectedTiFlashGroup(ctx, tiflashGroupCr, apiv1.ComponentType_TIFLASH_COMPUTE, options...)
	if err != nil {
		return nil, fmt.Errorf("failed to get expected tiflash cn %s: %w", groupId, err)
	}

	if needUpdate {
		if err := remoteCli.Update(ctx, tiflashGroupCr); err != nil {
			return nil, status.Errorf(codes.Internal, "failed to update tiflash cn cr %s: %s", groupId, err)
		}
	}

	tiflashComps, err := cvtTiFlashComponentToEn(ctx, tiflashGroupCr)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to convert tiflash cn %s: %s", groupId, err)
	}

	// tiflash have tiflash learner , so we need to filter it
	for _, tiflash := range tiflashComps {
		if tiflash.GetComponentType() == apiv1.ComponentType_TIFLASH_COMPUTE {
			return tiflash, nil
		}
	}

	return nil, status.Errorf(codes.Internal, "failed to find tiflash cn component after update")
}

func ListTiFlashWNs(ctx context.Context, remoteCli client.Client, clusterId string) ([]*entity.Component, error) {
	tiflashGroups, err := listTiFlashGroups(ctx, remoteCli, dataplaneNamespace(clusterId))
	if err != nil {
		return nil, err
	}
	tiflashGroups = lo.Filter(tiflashGroups, func(comp *entity.Component, _ int) bool {
		return comp.GetComponentType() == apiv1.ComponentType_TIFLASH_WRITE
	})
	return tiflashGroups, nil
}

func GetTiFlashWN(ctx context.Context, remoteCli client.Client, clusterId, groupId string) (*entity.Component, error) {
	tiflashGroupCr, err := getTiFlashWNCR(ctx, remoteCli, dataplaneNamespace(clusterId), groupId)
	if err != nil {
		if apierrors.IsNotFound(err) {
			return nil, status.Errorf(codes.NotFound, "tiflash wn %s not found: %s", groupId, err)
		}
		return nil, status.Errorf(codes.Internal, "failed to get tiflash wn %s: %s", groupId, err)
	}
	tiflashComps, err := cvtTiFlashComponentToEn(ctx, tiflashGroupCr)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to convert tiflash wn %s: %s", groupId, err)
	}

	// tiflash have tiflash learner , so we need to filter it
	for _, tiflash := range tiflashComps {
		if tiflash.GetComponentType() == apiv1.ComponentType_TIFLASH_WRITE {
			return tiflash, nil
		}
	}
	// If no tiflash wn found, return not found error
	return nil, status.Errorf(codes.NotFound, "tiflash wn %s not found", groupId)
}

func UpdateTiFlashWN(ctx context.Context, remoteCli client.Client, clusterId, groupId string, options []UpdateOption) (*entity.Component, error) {
	tiflashGroupCr, err := getTiFlashWNCR(ctx, remoteCli, dataplaneNamespace(clusterId), groupId)
	if err != nil {
		if apierrors.IsNotFound(err) {
			return nil, status.Errorf(codes.NotFound, "tiflash wn %s not found: %s", groupId, err)
		}
		return nil, status.Errorf(codes.Internal, "failed to get tiflash wn %s: %s", groupId, err)
	}

	needUpdate, err := getUpdateExpectedTiFlashGroup(ctx, tiflashGroupCr, apiv1.ComponentType_TIFLASH_WRITE, options...)
	if err != nil {
		return nil, fmt.Errorf("failed to get expected tiflash wn %s: %w", groupId, err)
	}

	if needUpdate {
		if err := remoteCli.Update(ctx, tiflashGroupCr); err != nil {
			return nil, status.Errorf(codes.Internal, "failed to update tiflash wn cr %s: %s", groupId, err)
		}
	}

	tiflashComps, err := cvtTiFlashComponentToEn(ctx, tiflashGroupCr)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to convert tiflash wn %s: %s", groupId, err)
	}

	// tiflash have tiflash learner , so we need to filter it
	for _, tiflash := range tiflashComps {
		if tiflash.GetComponentType() == apiv1.ComponentType_TIFLASH_WRITE {
			return tiflash, nil
		}
	}

	return nil, status.Errorf(codes.Internal, "failed to find tiflash wn component after update")
}

func getTiFlashCNCR(ctx context.Context, remoteCli client.Client, dataplaneNs, groupId string) (*v1alpha1.TiFlashGroup, error) {
	if !strings.HasPrefix(groupId, componentGroupPrefix) {
		groupId = fmt.Sprintf("%s%s", componentGroupPrefix, groupId)
	}

	// get tiflash compute group
	var tiflashGroup v1alpha1.TiFlashGroup
	key := types.NamespacedName{Name: fmt.Sprintf("%s-compute", groupId), Namespace: dataplaneNs}

	if err := remoteCli.Get(
		ctx, key, &tiflashGroup,
	); err != nil {
		return nil, err
	}

	return &tiflashGroup, nil
}

func getTiFlashWNCR(ctx context.Context, remoteCli client.Client, dataplaneNs, groupId string) (*v1alpha1.TiFlashGroup, error) {
	if !strings.HasPrefix(groupId, componentGroupPrefix) {
		groupId = fmt.Sprintf("%s%s", componentGroupPrefix, groupId)
	}

	// get tiflash write group
	var tiflashGroup v1alpha1.TiFlashGroup
	key := types.NamespacedName{Name: fmt.Sprintf("%s-write", groupId), Namespace: dataplaneNs}

	if err := remoteCli.Get(
		ctx, key, &tiflashGroup,
	); err != nil {
		return nil, err
	}

	return &tiflashGroup, nil
}

func listTiFlashGroups(ctx context.Context, remoteClient client.Client, dataplaneNs string) ([]*entity.Component, error) {
	// get all component groups
	var tiflashGroups v1alpha1.TiFlashGroupList
	if err := remoteClient.List(ctx, &tiflashGroups, client.InNamespace(dataplaneNs)); err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list tiflash groups: %s", err)
	}

	tiflashComponents := make([]*entity.Component, 0, len(tiflashGroups.Items))

	for i := range tiflashGroups.Items {
		compEns, err := cvtTiFlashComponentToEn(ctx, &tiflashGroups.Items[i])

		if err != nil {
			tilog.Warn(ctx, "convert tiflash group to component", zap.Error(err))
		} else {
			tiflashComponents = append(tiflashComponents, compEns...)
		}
	}
	return tiflashComponents, nil
}

func cvtTiFlashComponentToEn(ctx context.Context, tiflashGroup *v1alpha1.TiFlashGroup) ([]*entity.Component, error) {
	compEn := &entity.Component{
		Id: strings.Split(tiflashGroup.Name, "-")[1], // db-<Id>-tiflash
	}
	setTemplateVersionFromAnnotations(compEn, tiflashGroup.Annotations)

	switch tiflashGroup.Labels[schemav1.LabelComponentKey] {
	case entity.TiFlashComputeType:
		compEn.ComponentType = apiv1.ComponentType_TIFLASH_COMPUTE
	case entity.TiFlashWriteType:
		compEn.ComponentType = apiv1.ComponentType_TIFLASH_WRITE
	default:
		return nil, fmt.Errorf("unknown tiflash group component type: %s", tiflashGroup.Labels[schemav1.LabelComponentKey])
	}

	var err error
	var tiflashContainer corev1.Container
	foundContainer := false
	if tiflashGroup.Spec.Template.Spec.Overlay != nil &&
		tiflashGroup.Spec.Template.Spec.Overlay.Pod != nil {
		for i := range tiflashGroup.Spec.Template.Spec.Overlay.Pod.Spec.Containers {
			if tiflashGroup.Spec.Template.Spec.Overlay.Pod.Spec.Containers[i].Name == "tiflash" {
				tiflashContainer = tiflashGroup.Spec.Template.Spec.Overlay.Pod.Spec.Containers[i]
				foundContainer = true
				break
			}
		}
	}
	if !foundContainer {
		return nil, fmt.Errorf("container named 'tiflash' not found in TiFlashGroup %s", tiflashGroup.Name)
	}

	// Get nodeSelector from Overlay if present
	var nodeSelector map[string]string
	if tiflashGroup.Spec.Template.Spec.Overlay != nil &&
		tiflashGroup.Spec.Template.Spec.Overlay.Pod != nil {
		nodeSelector = tiflashGroup.Spec.Template.Spec.Overlay.Pod.Spec.NodeSelector
	}

	compEn.NodeQuota, err = CvtNodeSpecToQuota(
		tiflashGroup.Labels,
		tiflashGroup.Annotations,
		uint32(*tiflashGroup.Spec.Replicas),
		tiflashContainer.Resources.Limits,
		nodeSelector,
	)
	if err != nil {
		return nil, fmt.Errorf("failed to get tiflash group %s quota: %w", compEn.GetId(), err)
	}
	compEn.Version = tiflashGroup.Spec.Template.Spec.Version

	imageRepo, imageTag := splitImageRepoTag(tiflashGroup.Spec.Template.Spec.Image)
	compEn.Configuration = &entity.ComponentConfiguration{
		ImageRepo:                imageRepo,
		ImageTag:                 imageTag,
		ConfigurationFileContent: string(tiflashGroup.Spec.Template.Spec.Config),
	}

	compEn.Phase = getTiFlashGroupStatus(ctx, tiflashGroup)

	compEn.Storages, err = cvtStoragesToEn(ctx, tiflashGroup.Spec.Template.Spec.Volumes)
	if err != nil {
		return nil, err
	}
	// set labels from server labels
	if tiflashGroup.Spec.Template.Spec.Server.Labels != nil {
		compEn.Labels = tiflashGroup.Spec.Template.Spec.Server.Labels
	}

	switch compEn.GetComponentType() {
	case apiv1.ComponentType_TIFLASH_WRITE:
		compLearnerEn := &entity.Component{
			Id:            strings.Split(tiflashGroup.Name, "-")[1], // db-<Id>-tiflash
			ComponentType: apiv1.ComponentType_TIFLASH_LEARNER,
			Version:       tiflashGroup.Spec.Template.Spec.Version,
			Configuration: &entity.ComponentConfiguration{
				ConfigurationFileContent: string(tiflashGroup.Spec.Template.Spec.ProxyConfig),
			},
			Phase: compEn.Phase,
		}
		setTemplateVersionFromAnnotations(compLearnerEn, tiflashGroup.Annotations)
		return []*entity.Component{compEn, compLearnerEn}, nil
	case apiv1.ComponentType_TIFLASH_COMPUTE:
		// only tiflash compute
		compLearnerEn := &entity.Component{
			Id:            strings.Split(tiflashGroup.Name, "-")[1], // db-<Id>-tiflash
			ComponentType: apiv1.ComponentType_TIFLASH_COMPUTE_LEARNER,
			Version:       tiflashGroup.Spec.Template.Spec.Version,
			Configuration: &entity.ComponentConfiguration{
				ConfigurationFileContent: string(tiflashGroup.Spec.Template.Spec.ProxyConfig),
			},
			Phase: compEn.Phase,
		}
		setTemplateVersionFromAnnotations(compLearnerEn, tiflashGroup.Annotations)
		return []*entity.Component{compEn, compLearnerEn}, nil
	}
	return []*entity.Component{compEn}, nil
}

func getTiFlashGroupStatus(ctx context.Context, tiflashGroup *v1alpha1.TiFlashGroup) apiv1.Component_Phase {
	if !isSynced(tiflashGroup.Status.Conditions) {
		tilog.Warn(ctx, "tiflashGroup is not synced", zap.Any("Conditions", tiflashGroup.Status.Conditions))
		return apiv1.Component_MODIFYING
	}

	if !isUpToDate(tiflashGroup.Generation, tiflashGroup.Status.CommonStatus) {
		tilog.Warn(ctx, "tiflashGroup is not up to date", zap.Any("Status", tiflashGroup.Status))
		return apiv1.Component_MODIFYING
	}

	if (tiflashGroup.Status.ReadyReplicas == 0 || tiflashGroup.Status.UpdatedReplicas == 0) &&
		*tiflashGroup.Spec.Replicas > 0 {
		tilog.Warn(ctx, "tiflashGroup has zero ready or updated replicas", zap.Int32("ReadyReplicas", tiflashGroup.Status.ReadyReplicas), zap.Int32("UpdatedReplicas", tiflashGroup.Status.UpdatedReplicas))
		return apiv1.Component_UNAVAILABLE
	}

	// check if tiflashGroup is ready
	if !isReady(tiflashGroup.Status.Conditions) {
		tilog.Warn(ctx, "tiflashGroup is not ready", zap.Any("Conditions", tiflashGroup.Status.Conditions))
		return apiv1.Component_UNAVAILABLE
	}

	return apiv1.Component_AVAILABLE
}

func getUpdateExpectedTiFlashGroup(ctx context.Context, tiflashGroupCr *v1alpha1.TiFlashGroup, compType apiv1.ComponentType, options ...UpdateOption) (bool, error) {
	var needUpdate bool
	for _, opt := range options {
		switch opt.Option {
		case OpsUpdateNodeCount:
			count, ok := opt.Value.(uint32)
			if !ok {
				tilog.Error(ctx, "invalid node count", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid node count for tiflash group %s", tiflashGroupCr.Name)
			}

			if count == 0 {
				tilog.Warn(ctx, "tiflash group node count is zero, scaling to zero", zap.String("tiflashGroup", tiflashGroupCr.Name))
			}

			currNodeQuota, err := CvtNodeSpecToQuota(tiflashGroupCr.Labels, tiflashGroupCr.Annotations, uint32(*tiflashGroupCr.Spec.Replicas), nil, nil)
			if err != nil {
				return false, status.Errorf(codes.Internal, "failed to convert node spec to quota for tiflash group %s: %s", tiflashGroupCr.Name, err)
			}

			nodeQuota := &entity.NodeQuota{
				NodeCount: count,
			}

			err = checkNodeQuota(currNodeQuota, nodeQuota)
			if err != nil {
				return false, status.Errorf(codes.InvalidArgument, "invalid node quota for tiflash group %s: %s", tiflashGroupCr.Name, err)
			}

			if uint32(*tiflashGroupCr.Spec.Replicas) != count {
				tiflashGroupCr.Spec.Replicas = lo.ToPtr(int32(count))
				needUpdate = true
			}

		case OpsUpdateNodeSpec:
			nodeSpec, ok := opt.Value.(*entity.NodeSpec)
			if !ok {
				tilog.Error(ctx, "invalid node spec", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid node spec for tiflash group %s", tiflashGroupCr.Name)
			}

			resourceRequirements, err := setNodeSpec(nodeSpec, tiflashGroupCr.Labels, tiflashGroupCr.Spec.Template.Spec.Overlay.Pod.Spec.NodeSelector, compType)
			if err != nil {
				tilog.Error(ctx, "set node spec", zap.Error(err))
				return false, fmt.Errorf("failed to set node spec for tiflash group %s: %w", tiflashGroupCr.Name, err)
			}
			for i := range tiflashGroupCr.Spec.Template.Spec.Overlay.Pod.Spec.Containers {
				if tiflashGroupCr.Spec.Template.Spec.Overlay.Pod.Spec.Containers[i].Name == "tiflash" {
					tiflashGroupCr.Spec.Template.Spec.Overlay.Pod.Spec.Containers[i].Resources = resourceRequirements

					needUpdate = true
					break
				}
			}
		case OpsUpdateMaxCount:
			maxCount, ok := opt.Value.(uint32)
			if !ok {
				tilog.Error(ctx, "invalid max count", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid max count for tiflash group %s", tiflashGroupCr.Name)
			}
			if maxCount > 0 {
				annotations := tiflashGroupCr.Annotations
				if annotations == nil {
					annotations = map[string]string{}
				}

				annotations[schemav1.AnnotationsComponentMaxCount] = fmt.Sprintf("%d", maxCount)
				tiflashGroupCr.Annotations = annotations

				needUpdate = true
			}
		case OpsUpdateMinCount:
			minCount, ok := opt.Value.(uint32)
			if !ok {
				tilog.Error(ctx, "invalid min count", zap.Any("value", opt.Value))
			}

			annotations := tiflashGroupCr.Annotations
			if annotations == nil {
				annotations = map[string]string{}
			}

			annotations[schemav1.AnnotationsComponentMinCount] = fmt.Sprintf("%d", minCount)
			tiflashGroupCr.Annotations = annotations

			needUpdate = true
		case OpsUpdateVersion:
			version, ok := opt.Value.(string)
			if !ok {
				tilog.Error(ctx, "invalid version", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid version for tiflash group %s", tiflashGroupCr.Name)
			}
			if version != "" {
				tiflashGroupCr.Spec.Template.Spec.Version = version
				needUpdate = true
			}
		case OpsUpdateImage:
			image, ok := opt.Value.(string)
			if !ok {
				tilog.Error(ctx, "invalid image", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid image for tiflash group %s", tiflashGroupCr.Name)
			}
			if image != "" {
				tiflashGroupCr.Spec.Template.Spec.Image = &image
				needUpdate = true
			}
		case OpsUpdateConfig:
			config, ok := opt.Value.(string)
			if !ok {
				tilog.Error(ctx, "invalid config", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid config for tiflash group %s", tiflashGroupCr.Name)
			}
			if config != "" {
				tiflashGroupCr.Spec.Template.Spec.Config = v1alpha1.ConfigFile(config)
				needUpdate = true
			}
		case OpsUpdateTiFlashLeaderConfig:
			// only write node can update leader config
			config, ok := opt.Value.(string)
			if !ok {
				tilog.Error(ctx, "invalid leader config", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid leader config for tiflash group %s", tiflashGroupCr.Name)
			}
			// Only update ProxyConfig for write nodes
			if config != "" && tiflashGroupCr.Labels[schemav1.LabelComponentKey] == entity.TiFlashWriteType {
				tiflashGroupCr.Spec.Template.Spec.ProxyConfig = v1alpha1.ConfigFile(config)
				needUpdate = true
			}
		case OpsUpdateUpdateStrategy:
			strategy, ok := opt.Value.(v1alpha1.ConfigUpdateStrategy)
			if !ok {
				tilog.Error(ctx, "invalid update strategy", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid update strategy for tiflash group %s", tiflashGroupCr.Name)
			}
			if strategy != "" {
				tiflashGroupCr.Spec.Template.Spec.UpdateStrategy.Config = strategy
				needUpdate = true
			}
		case OpsUpdateLabels:
			labels, ok := opt.Value.(map[string]string)
			if !ok {
				tilog.Error(ctx, "invalid labels", zap.Any("value", opt.Value))
				return false, fmt.Errorf("invalid labels for tiflash group %s", tiflashGroupCr.Name)
			}
			if labels != nil {
				serverLabels := tiflashGroupCr.Spec.Template.Spec.Server.Labels
				if serverLabels == nil {
					serverLabels = map[string]string{}
				}
				// merge labels
				for k, v := range labels {
					serverLabels[k] = v
				}
				tiflashGroupCr.Spec.Template.Spec.Server.Labels = serverLabels
				needUpdate = true
			}
		case OpsUpdateNodeVersion:
			nodeVersion, ok := opt.Value.(string)
			if !ok || nodeVersion == "" {
				tilog.Error(ctx, "invalid node version", zap.Any("value", opt.Value))
				return false, status.Errorf(codes.InvalidArgument, "invalid node version for tiflash group %s", tiflashGroupCr.Name)
			}
			nodeSelector := tiflashGroupCr.Spec.Template.Spec.Overlay.Pod.Spec.NodeSelector
			if nodeSelector == nil {
				nodeSelector = map[string]string{}
			}
			if nodeSelector[schemav1.K8sVersionLabelKey] != nodeVersion {
				nodeSelector[schemav1.K8sVersionLabelKey] = nodeVersion
				tiflashGroupCr.Spec.Template.Spec.Overlay.Pod.Spec.NodeSelector = nodeSelector
				needUpdate = true
			}
		}
	}

	for i := range tiflashGroupCr.Spec.Template.Spec.Volumes {
		// data
		if tiflashGroupCr.Spec.Template.Spec.Volumes[i].Name == string(schemav1.StorageData) {
			modifyVolume := false
			for _, opt := range options {
				switch opt.Option {
				case OpsUpdateDataSize:
					size, ok := opt.Value.(uint32)
					if !ok {
						tilog.Error(ctx, "invalid data storage size", zap.Any("value", opt.Value))
						return false, fmt.Errorf("invalid data storage size for tiflash group %s", tiflashGroupCr.Name)
					}
					sizeQuantity := resource.MustParse(fmt.Sprintf("%dGi", size))
					if size > 0 && tiflashGroupCr.Spec.Template.Spec.Volumes[i].Storage.Cmp(sizeQuantity) < 0 {
						tiflashGroupCr.Spec.Template.Spec.Volumes[i].Storage = sizeQuantity
						modifyVolume = true
						needUpdate = true
					}

				case OpsUpdateDataVolumeAttributesClass:
					volumeAttributesClass, ok := opt.Value.(string)
					if !ok {
						tilog.Error(ctx, "invalid data volume attributes class", zap.Any("value", opt.Value))
						return false, fmt.Errorf("invalid data volume attributes class for tiflash group %s", tiflashGroupCr.Name)
					}
					curr := lo.FromPtr(tiflashGroupCr.Spec.Template.Spec.Volumes[i].VolumeAttributesClassName)
					if volumeAttributesClass != "" && volumeAttributesClass != curr {
						tiflashGroupCr.Spec.Template.Spec.Volumes[i].VolumeAttributesClassName = lo.ToPtr(strings.ToLower(volumeAttributesClass))
						modifyVolume = true
						needUpdate = true
					}
				}
			}
			if modifyVolume {
				if tiflashGroupCr.Annotations == nil {
					tiflashGroupCr.Annotations = make(map[string]string)
				}
				tiflashGroupCr.Annotations[schemav1.AnnotationsUpdateTimeDataVolumeKey] = time.Now().Format(time.RFC3339)
			}
		}
	}

	return needUpdate, nil
}
