package v1

import (
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/types"
)

const (
	DefaultPublicSQLEndpointPort  = int32(4000)
	DefaultPrivateSQLEndpointPort = int32(4000)
)

type ComponentType string

const (
	ComponentTiDB           ComponentType = "tidb"
	ComponentTiKV           ComponentType = "tikv"
	ComponentPD             ComponentType = "pd"
	ComponentTiFlash        ComponentType = "tiflash"
	ComponentTiFlashLearner ComponentType = "tiflash-learner" // TiflashLearner is used to define TiFlash Proxy config
	ComponentRestore        ComponentType = "restore"
	ComponentBackup         ComponentType = "backup"
	ComponentTiCDC          ComponentType = "ticdc"
	ComponentAdminTiDB      ComponentType = "extra-tidb"
)

// PrefixedId return a identifier of the cluster prefixed by "tidb", which is used as a lot of reference key
// in our legacy system
func (c *Cluster) PrefixedId() string {
	return ClusterPrefixId(c.Name)
}

// ClusterPrefixId return an identifier of the cluster prefixed by "tidb", which is used as a lot of reference key
// in our legacy system
func ClusterPrefixId(clusterName string) string {
	return fmt.Sprintf("tidb%s", clusterName)
}

func (c *Cluster) GetProviderType() string {
	return c.Labels[ProviderTypeLabel]
}

func (c *Cluster) GetComponents() []ClusterComponent {
	comps := []ClusterComponent{&c.Spec.Cluster.PD, &c.Spec.Cluster.TiKV, &c.Spec.Cluster.TiDB}
	if c.Spec.Cluster.TiFlash.Replicas > 0 {
		comps = append(comps, &c.Spec.Cluster.TiFlash)
	}
	return comps
}

// GetSuspendCondition returns the suspend cluster condition
func (c *Cluster) GetSuspendCondition() *ClusterCondition {
	for _, condition := range c.Status.Conditions {
		if condition.Type == ClusterSuspended {
			return &condition
		}
	}
	return nil
}

// Suspended returns true if all the components has been suspended
func (c *Cluster) Suspended() bool {
	return IsStatusConditionTrue(c.Status.Conditions, ClusterSuspended)
}

// Suspending returns true if any one of the components is suspending
func (c *Cluster) Suspending() bool {
	return (!c.PDStatusNil() && c.Status.DB.PD.Suspending()) ||
		(!c.TiDBStatusNil() && c.Status.DB.TiDB.Suspending()) ||
		(!c.TiKVStatusNil() && c.Status.DB.TiKV.Suspending()) ||
		(!c.TiFlashStatusNil() && c.Status.DB.TiFlash.Suspending())
}
func (c *Cluster) PDStatusNil() bool {
	if c.Status.DB == nil {
		return true
	}
	if c.Status.DB.PD == nil {
		return true
	}
	return false
}
func (c *Cluster) TiDBStatusNil() bool {
	if c.Status.DB == nil {
		return true
	}
	if c.Status.DB.TiDB == nil {
		return true
	}
	return false
}
func (c *Cluster) TiKVStatusNil() bool {
	if c.Status.DB == nil {
		return true
	}
	if c.Status.DB.TiKV == nil {
		return true
	}
	return false
}
func (c *Cluster) TiFlashStatusNil() bool {
	if c.Status.DB == nil {
		return true
	}
	if c.Status.DB.TiFlash == nil {
		return true
	}
	return false
}

// Synced make sure the status is synced as the spec.
// we can use it to make sure the update is applied when we update the replica, strorage or instance in the spec.
func (c *Cluster) Synced() bool {
	if c.Status.Phase != ClusterPhaseRunning {
		return false
	}

	db := c.Status.DB
	if db == nil {
		return false
	}

	if c.Spec.Cluster.TiDB.Replicas == 0 {
		if db.TiDB != nil && len(db.TiDB.Members) != 0 {
			return false
		}
	} else {
		if db.TiDB == nil || db.TiDB.Status != ComponentNormal || len(db.TiDB.Members) != c.Spec.Cluster.TiDB.Replicas {
			return false
		}
		for _, member := range db.TiDB.Members {
			if member.InstanceType != "" && member.InstanceType != c.Spec.Cluster.TiDB.InstanceType {
				// NOTE: we treat `member.InstanceType == ""` as Synced as some providers not implment instance type scale.
				// check `InstanceType` rather than `InstanceTypeScalePhase` so that we can report Synced correctly before provider reconcile.
				return false
			}
		}
		if db.TiDB.InstanceTypeScalePhase != InstanceTypeScalePhaseNone {
			return false
		}
	}

	if c.Spec.Cluster.TiKV.Replicas == 0 {
		if db.TiKV != nil && len(db.TiKV.Members) != 0 {
			return false
		}
	} else {
		if db.TiKV == nil || db.TiKV.Status != ComponentNormal || len(db.TiKV.Members) != c.Spec.Cluster.TiKV.Replicas {
			return false
		}
		if !c.Spec.Cluster.TiKV.StorageSize.IsZero() && !c.Spec.Cluster.TiKV.GetUseLocalStorage() {
			if db.TiKV.Volume != nil && (c.Spec.Cluster.TiKV.StorageSize.Cmp(db.TiKV.Volume.CurrentCapacity) != 0 ||
				!db.TiKV.Volume.storageClassSynced(c.Spec.Cluster.TiKV.StorageClassName)) {
				return false
			}
		}
		for _, member := range db.TiKV.Members {
			if member.InstanceType != "" && member.InstanceType != c.Spec.Cluster.TiKV.InstanceType {
				return false
			}
		}
		if db.TiKV.InstanceTypeScalePhase != InstanceTypeScalePhaseNone {
			return false
		}
	}

	if c.Spec.Cluster.PD.Replicas == 0 {
		if db.PD != nil && len(db.PD.Members) != 0 {
			return false
		}
	} else {
		if db.PD == nil || db.PD.Status != ComponentNormal || len(db.PD.Members) != c.Spec.Cluster.PD.Replicas {
			return false
		}
		if !c.Spec.Cluster.PD.StorageSize.IsZero() && !c.Spec.Cluster.PD.GetUseLocalStorage() {
			if db.PD.Volume != nil && (c.Spec.Cluster.PD.StorageSize.Cmp(db.PD.Volume.CurrentCapacity) != 0 ||
				!db.PD.Volume.storageClassSynced(c.Spec.Cluster.PD.StorageClassName)) {
				return false
			}
		}
		for _, member := range db.PD.Members {
			if member.InstanceType != "" && member.InstanceType != c.Spec.Cluster.PD.InstanceType {
				return false
			}
		}
		if db.PD.InstanceTypeScalePhase != InstanceTypeScalePhaseNone {
			return false
		}
	}

	if c.Spec.Cluster.TiFlash.Replicas == 0 {
		if db.TiFlash != nil && len(db.TiFlash.Members) != 0 {
			return false
		}
	} else {
		if db.TiFlash == nil || db.TiFlash.Status != ComponentNormal || len(db.TiFlash.Members) != c.Spec.Cluster.TiFlash.Replicas {
			return false
		}
		if !c.Spec.Cluster.TiFlash.StorageSize.IsZero() && !c.Spec.Cluster.TiFlash.GetUseLocalStorage() {
			if db.TiFlash.Volume != nil && (c.Spec.Cluster.TiFlash.StorageSize.Cmp(db.TiFlash.Volume.CurrentCapacity) != 0 ||
				!db.TiFlash.Volume.storageClassSynced(c.Spec.Cluster.TiFlash.StorageClassName)) {
				return false
			}
		}
		for _, member := range db.TiFlash.Members {
			if member.InstanceType != "" && member.InstanceType != c.Spec.Cluster.TiFlash.InstanceType {
				return false
			}
		}
		if db.TiFlash.InstanceTypeScalePhase != InstanceTypeScalePhaseNone {
			return false
		}
	}

	return true
}

func (c *Cluster) GetNetworkRef() types.NamespacedName {
	return types.NamespacedName{
		Namespace: c.Namespace,
		Name:      c.Spec.NetworkName,
	}
}

func (c *Cluster) ClusterScopeName() string {
	return fmt.Sprintf("%s-%s", c.Namespace, c.Name)
}

func (c *Cluster) GetPublicSQLEndpointPort() int32 {
	port := DefaultPublicSQLEndpointPort
	if c.Spec.SqlEndpoints.Public.Port != nil {
		port = *c.Spec.SqlEndpoints.Public.Port
	}
	return port
}

func (c *Cluster) GetPrivateSQLEndpointPort() int32 {
	port := DefaultPrivateSQLEndpointPort
	if c.Spec.SqlEndpoints.Private != nil && c.Spec.SqlEndpoints.Private.Port != nil {
		port = *c.Spec.SqlEndpoints.Private.Port
	}
	return port
}

// GetUpToDateReplicas returns the replicas count which has been up to date.
// some special cases:
// - scale up: the member in TiDB Cluster becomes disconnected delay and the new pod may already started in the new instance
// - scale out + scale up: scaled out replicas with new instance-type and become normal before rolling-update started
// A member can only be marked as up to date until the member becomes normal again in the new instance,
// but we can't handle this accurately and only ensure:
// - the progress never changed back
// - all members become normal when progress reached 100%
func (c *Cluster) GetUpToDateReplicas(comp ComponentType) int {
	db := c.Status.DB
	if db == nil {
		return 0 // "0" if no status exists yet
	}

	replicas := 0
	switch comp {
	case ComponentPD:
		if db.PD == nil {
			return replicas // "0" if no status exists yet
		}

		if !c.Spec.Cluster.PD.StorageSize.IsZero() && !c.Spec.Cluster.PD.GetUseLocalStorage() {
			if db.PD.Volume != nil && (db.PD.Volume.storageSizeSyncPending(c.Spec.Cluster.PD.StorageSize) ||
				db.PD.Volume.storageClassSyncPending(c.Spec.Cluster.PD.StorageClassName)) {
				replicas = 0 // storage size/class scale not started yet
				break
			}
		}

		itUpdatedReplicas := 0
		for _, member := range db.PD.Members {
			// When scale up + scale out, the new member will become normal before rolling-update started (then becomes unavailable),
			// in order to avoid progress decrease, we should tolerate the unavailable status.
			if (member.InstanceType == "" || member.InstanceType == c.Spec.Cluster.PD.InstanceType) &&
				(member.Status == MemberStatusNormal || member.Status == MemberStatusUnavailable) {
				itUpdatedReplicas++
			}
		}
		if itUpdatedReplicas == 0 {
			replicas = 0 // instance type scale not started yet
			break
		}

		replicas = int(db.PD.UpdatedReplicas)
		// as no volume status for each member to check whether its volume is up to date,
		// so we choose the min of updated-replicas (both StatefulSet and compute/instance) and volume-resized-count as the up to date replicas.
		if itUpdatedReplicas < replicas {
			replicas = itUpdatedReplicas
		}
		if db.PD.Volume != nil && db.PD.Volume.getModifiedCount() < replicas {
			replicas = db.PD.Volume.getModifiedCount()
		}
		if replicas == c.Spec.Cluster.PD.Replicas && db.PD.Status != ComponentNormal {
			if c.Spec.Cluster.PD.Replicas >= len(db.PD.Members) {
				// HACK: do not reach 100% if still scaling (when scale out, this may be Unhealthy),
				// but for scaling in (only), it should not less than 100%.
				// As we scale-in first before upgrade (for instance-type or storage) in TiDB-Operator,
				// if `replicas == c.Spec.Cluster.PD.Replicas` && `.Spec.Cluster.PD.Replicas < len(db.PD.Members)`,
				// that should be scaling-in only.
				replicas = replicas - 1
			}
		}
	case ComponentTiKV:
		if db.TiKV == nil {
			return replicas
		}

		if !c.Spec.Cluster.TiKV.StorageSize.IsZero() && !c.Spec.Cluster.TiKV.GetUseLocalStorage() {
			if db.TiKV.Volume != nil && (db.TiKV.Volume.storageSizeSyncPending(c.Spec.Cluster.TiKV.StorageSize) ||
				db.TiKV.Volume.storageClassSyncPending(c.Spec.Cluster.TiKV.StorageClassName)) {
				replicas = 0
				break
			}
		}

		itUpdatedReplicas := 0
		for _, member := range db.TiKV.Members {
			if (member.InstanceType == "" || member.InstanceType == c.Spec.Cluster.TiKV.InstanceType) &&
				(member.Status == MemberStatusNormal || member.Status == MemberStatusUnavailable) {
				itUpdatedReplicas++
			}
		}
		if itUpdatedReplicas == 0 {
			replicas = 0
			break
		}

		replicas = int(db.TiKV.UpdatedReplicas)
		if itUpdatedReplicas < replicas {
			replicas = itUpdatedReplicas
		}
		if db.TiKV.Volume != nil && db.TiKV.Volume.getModifiedCount() < replicas {
			replicas = db.TiKV.Volume.getModifiedCount()
		}

		if replicas == c.Spec.Cluster.TiKV.Replicas && db.TiKV.Status != ComponentNormal {
			if c.Spec.Cluster.TiKV.Replicas >= len(db.TiKV.Members) {
				replicas = replicas - 1
			}
		}
	case ComponentTiDB:
		if db.TiDB == nil {
			return replicas
		}

		itUpdatedReplicas := 0
		for _, member := range db.TiDB.Members {
			if (member.InstanceType == "" || member.InstanceType == c.Spec.Cluster.TiDB.InstanceType) &&
				(member.Status == MemberStatusNormal || member.Status == MemberStatusUnavailable) {
				itUpdatedReplicas++
			}
		}
		if itUpdatedReplicas == 0 {
			replicas = 0
			break
		}

		replicas = int(db.TiDB.UpdatedReplicas)
		if itUpdatedReplicas < replicas {
			replicas = itUpdatedReplicas
		}
		if replicas == c.Spec.Cluster.TiDB.Replicas && db.TiDB.Status != ComponentNormal {
			if c.Spec.Cluster.TiDB.Replicas >= len(db.TiDB.Members) {
				replicas = replicas - 1
			}
		}
	case ComponentTiFlash:
		if db.TiFlash == nil {
			return replicas
		}

		if !c.Spec.Cluster.TiFlash.StorageSize.IsZero() && !c.Spec.Cluster.TiFlash.GetUseLocalStorage() {
			if db.TiFlash.Volume != nil && (db.TiFlash.Volume.storageSizeSyncPending(c.Spec.Cluster.TiFlash.StorageSize) ||
				db.TiFlash.Volume.storageClassSyncPending(c.Spec.Cluster.TiFlash.StorageClassName)) {
				replicas = 0
				break
			}
		}

		itUpdatedReplicas := 0
		for _, member := range db.TiFlash.Members {
			if (member.InstanceType == "" || member.InstanceType == c.Spec.Cluster.TiFlash.InstanceType) &&
				(member.Status == MemberStatusNormal || member.Status == MemberStatusUnavailable) {
				itUpdatedReplicas++
			}
		}
		if itUpdatedReplicas == 0 {
			replicas = 0
			break
		}

		replicas = int(db.TiFlash.UpdatedReplicas)
		if itUpdatedReplicas < replicas {
			replicas = itUpdatedReplicas
		}
		if db.TiFlash.Volume != nil && db.TiFlash.Volume.getModifiedCount() < replicas {
			replicas = db.TiFlash.Volume.getModifiedCount()
		}
		if replicas == c.Spec.Cluster.TiFlash.Replicas && db.TiFlash.Status != ComponentNormal {
			if c.Spec.Cluster.TiFlash.Replicas >= len(db.TiFlash.Members) {
				replicas = replicas - 1
			}
		}
	}

	return replicas
}

// GetRemainingDeleteReplicas returns the replicas count which is still remaining to be deleted.
func (c *Cluster) GetRemainingDeleteReplicas(comp ComponentType) (count int) {
	db := c.Status.DB
	if db == nil {
		return 0 // "0" if no status exists yet
	}

	defer func() {
		if count < 0 {
			count = 0
		}
	}()

	switch comp {
	case ComponentPD:
		if db.PD == nil {
			return 0 // "0" if no status exists yet
		}
		return len(db.PD.Members) - c.Spec.Cluster.PD.Replicas
	case ComponentTiKV:
		if db.TiKV == nil {
			return 0
		}
		return len(db.TiKV.Members) - c.Spec.Cluster.TiKV.Replicas
	case ComponentTiDB:
		if db.TiDB == nil {
			return 0
		}
		return len(db.TiDB.Members) - c.Spec.Cluster.TiDB.Replicas
	case ComponentTiFlash:
		if db.TiFlash == nil {
			return 0
		}
		return len(db.TiFlash.Members) - c.Spec.Cluster.TiFlash.Replicas
	}
	return 0
}

func (v *VolumeStatus) getModifiedCount() int {
	if v.ModifiedCount == 0 {
		return v.ResizedCount
	}
	return v.ModifiedCount
}

func (v *VolumeStatus) storageSizeSyncPending(desired resource.Quantity) bool {
	modifiedCapacity := v.ModifiedCapacity
	if modifiedCapacity.IsZero() {
		modifiedCapacity = v.ResizedCapacity
	}
	return modifiedCapacity.Cmp(desired) != 0
}

func (v *VolumeStatus) storageClassSynced(desired string) bool {
	if v.CurrentStorageClass == "" || desired == "" {
		// special: no storage class in old voluem status
		// special: no storage class specified in spec
		return true
	}
	return v.CurrentStorageClass == desired
}

func (v *VolumeStatus) storageClassSyncPending(desired string) bool {
	if v.ModifiedStorageClass == "" || desired == "" {
		// special: no storage class in old voluem status
		// special: no storage class specified in spec
		return false
	}
	return v.ModifiedStorageClass != desired
}

func (s Service) IsEnabled() bool {
	return s.Enabled != nil && *s.Enabled
}

// +kubebuilder:object:root=false
// +kubebuilder:object:generate=false
type ClusterComponentStatus interface {
	Suspending() bool
	Suspended() bool
}

var _ ClusterComponentStatus = &PDStatus{}
var _ ClusterComponentStatus = &TiFlashStatus{}
var _ ClusterComponentStatus = &TiKVStatus{}
var _ ClusterComponentStatus = &TiDBStatus{}
var _ ClusterComponentStatus = &TiCDCStatus{}

func (s *PDStatus) Suspending() bool {
	return meta.IsStatusConditionFalse(s.Conditions, ComponentSuspended)
}
func (s *PDStatus) Suspended() bool {
	return meta.IsStatusConditionTrue(s.Conditions, ComponentSuspended)
}
func (s *TiKVStatus) Suspending() bool {
	return meta.IsStatusConditionFalse(s.Conditions, ComponentSuspended)
}
func (s *TiKVStatus) Suspended() bool {
	return meta.IsStatusConditionTrue(s.Conditions, ComponentSuspended)
}
func (s *TiDBStatus) Suspending() bool {
	return meta.IsStatusConditionFalse(s.Conditions, ComponentSuspended)
}
func (s *TiDBStatus) Suspended() bool {
	return meta.IsStatusConditionTrue(s.Conditions, ComponentSuspended)
}
func (s *TiFlashStatus) Suspending() bool {
	return meta.IsStatusConditionFalse(s.Conditions, ComponentSuspended)
}
func (s *TiFlashStatus) Suspended() bool {
	return meta.IsStatusConditionTrue(s.Conditions, ComponentSuspended)
}
func (s *TiCDCStatus) Suspending() bool {
	return false
}
func (s *TiCDCStatus) Suspended() bool {
	return false
}

// +kubebuilder:object:root=false
// +kubebuilder:object:generate=false
type ClusterComponent interface {
	GetReplicas() int
	GetInstanceType() string
	GetComponentType() ComponentType
	GetStorageClassName() (string, bool)
	GetStorageSize() (*resource.Quantity, bool)
	GetUseLocalStorage() bool
	NeedSuspend(*SuspendAction) bool
	GetSuspendActions(*SuspendAction) *SuspendAction

	SetReplicas(replicas int)
	SetInstanceType(instanceType string)
	SetStorageClassName(className string)
	SetStorageSize(size resource.Quantity)
	SetUseLocalStorage(useLocalStorage bool)
}

var _ ClusterComponent = &PD{}
var _ ClusterComponent = &TiFlash{}
var _ ClusterComponent = &TiKV{}
var _ ClusterComponent = &TiDB{}
var _ ClusterComponent = &TiCDC{}

func (c *PD) NeedSuspend(base *SuspendAction) bool {
	action := c.GetSuspendActions(base)
	if action != nil && action.SuspendCompute {
		return true
	}
	return false
}
func (c *PD) GetSuspendActions(base *SuspendAction) *SuspendAction {
	if c.SuspendAction != nil {
		return c.SuspendAction
	}
	return base
}
func (c *PD) GetReplicas() int {
	return c.Replicas
}

func (c *PD) GetInstanceType() string {
	return c.InstanceType
}

func (c *PD) GetComponentType() ComponentType {
	return ComponentPD
}

func (c *PD) GetStorageClassName() (string, bool) {
	return c.StorageClassName, true
}

func (c *PD) GetStorageSize() (*resource.Quantity, bool) {
	return &c.StorageSize, true
}

func (c *PD) GetUseLocalStorage() bool {
	return false
}

func (c *PD) SetReplicas(replicas int) {
	c.Replicas = replicas
}

func (c *PD) SetInstanceType(instanceType string) {
	c.InstanceType = instanceType
}

func (c *PD) SetStorageClassName(className string) {
	c.StorageClassName = className
}

func (c *PD) SetStorageSize(size resource.Quantity) {
	c.StorageSize = size
}

func (c *PD) SetUseLocalStorage(useLocalStorage bool) {
	panic("not supported")
}
func (c *TiKV) NeedSuspend(base *SuspendAction) bool {
	action := c.GetSuspendActions(base)
	if action != nil && action.SuspendCompute {
		return true
	}
	return false
}
func (c *TiKV) GetSuspendActions(base *SuspendAction) *SuspendAction {
	if c.SuspendAction != nil {
		return c.SuspendAction
	}
	return base
}
func (c *TiKV) GetReplicas() int {
	return c.Replicas
}

func (c *TiKV) GetInstanceType() string {
	return c.InstanceType
}

func (c *TiKV) GetComponentType() ComponentType {
	return ComponentTiKV
}

func (c *TiKV) GetStorageClassName() (string, bool) {
	return c.StorageClassName, true
}

func (c *TiKV) GetStorageSize() (*resource.Quantity, bool) {
	return &c.StorageSize, true
}

func (c *TiKV) GetUseLocalStorage() bool {
	if c.UseLocalStorage != nil {
		return *c.UseLocalStorage
	}
	return false
}

func (c *TiKV) SetReplicas(replicas int) {
	c.Replicas = replicas
}

func (c *TiKV) SetInstanceType(instanceType string) {
	c.InstanceType = instanceType
}

func (c *TiKV) SetStorageClassName(className string) {
	c.StorageClassName = className
}

func (c *TiKV) SetStorageSize(size resource.Quantity) {
	c.StorageSize = size
}

func (c *TiKV) SetUseLocalStorage(useLocalStorage bool) {
	c.UseLocalStorage = &useLocalStorage
}

func (c *TiFlash) GetReplicas() int {
	return c.Replicas
}
func (c *TiFlash) NeedSuspend(base *SuspendAction) bool {
	action := c.GetSuspendActions(base)
	if action != nil && action.SuspendCompute {
		return true
	}
	return false
}
func (c *TiFlash) GetSuspendActions(base *SuspendAction) *SuspendAction {
	if c.SuspendAction != nil {
		return c.SuspendAction
	}
	return base
}
func (c *TiFlash) GetInstanceType() string {
	return c.InstanceType
}

func (c *TiFlash) GetComponentType() ComponentType {
	return ComponentTiFlash
}

func (c *TiFlash) GetStorageClassName() (string, bool) {
	return c.StorageClassName, true
}

func (c *TiFlash) GetStorageSize() (*resource.Quantity, bool) {
	return &c.StorageSize, true
}

func (c *TiFlash) GetUseLocalStorage() bool {
	if c.UseLocalStorage != nil {
		return *c.UseLocalStorage
	}
	return false
}

func (c *TiFlash) SetReplicas(replicas int) {
	c.Replicas = replicas
}

func (c *TiFlash) SetInstanceType(instanceType string) {
	c.InstanceType = instanceType
}

func (c *TiFlash) SetStorageClassName(className string) {
	c.StorageClassName = className
}

func (c *TiFlash) SetStorageSize(size resource.Quantity) {
	c.StorageSize = size
}

func (c *TiFlash) SetUseLocalStorage(useLocalStorage bool) {
	c.UseLocalStorage = &useLocalStorage
}
func (c *TiDB) NeedSuspend(base *SuspendAction) bool {
	action := c.GetSuspendActions(base)
	if action != nil && action.SuspendCompute {
		return true
	}
	return false
}
func (c *TiDB) GetSuspendActions(base *SuspendAction) *SuspendAction {
	if c.SuspendAction != nil {
		return c.SuspendAction
	}
	return base
}
func (c *TiDB) GetReplicas() int {
	return c.Replicas
}

func (c *TiDB) GetInstanceType() string {
	return c.InstanceType
}

func (c *TiDB) GetComponentType() ComponentType {
	return ComponentTiDB
}

func (c *TiDB) GetStorageClassName() (string, bool) {
	return "", false
}

func (c *TiDB) GetStorageSize() (*resource.Quantity, bool) {
	return nil, false
}

func (c *TiDB) GetUseLocalStorage() bool {
	return false
}

func (c *TiDB) SetReplicas(replicas int) {
	c.Replicas = replicas
}

func (c *TiDB) SetInstanceType(instanceType string) {
	c.InstanceType = instanceType
}

func (c *TiDB) SetStorageClassName(className string) {
	panic("not supported")
}

func (c *TiDB) SetStorageSize(size resource.Quantity) {
	panic("not supported")
}

func (c *TiDB) SetUseLocalStorage(useLocalStorage bool) {
	panic("not supported")
}
