package config

import (
	"encoding/json"
	"fmt"
	"os"
	"sort"
	"strconv"
	"time"

	"helm.sh/helm/v3/pkg/chart"
	"helm.sh/helm/v3/pkg/chart/loader"
	storagev1 "k8s.io/api/storage/v1"
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/yaml"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/account"
	"github.com/tidbcloud/infra-provider/pkg/helm"
)

type ImageConfig struct {
	Registry string `json:"registry"`
	Repo     string `json:"repo"`
	Name     string `json:"name"`
	Tag      string `json:"tag"`
	Ref      string `json:"ref"`
}

type PatrolImageConfig struct {
	Tag string `json:"tag"`
}

type O11yAgentConfig struct {
	Image         ImageConfig `json:"image"`
	ReleaseName   string      `json:"releaseName"`
	Namespace     string      `json:"namespace"`
	HelmValues    map[string]interface{}
	HelmChartPath string `json:"chartPath"`
}

func (c *O11yAgentConfig) GetNamespace() string {
	return c.Namespace
}

func (c *O11yAgentConfig) GetName() string {
	return c.ReleaseName
}

func (c *O11yAgentConfig) GetCRDs() ([]*apiextensionsv1.CustomResourceDefinition, error) {
	return nil, nil
}

func (c *O11yAgentConfig) ToHelmChart() (result *chart.Chart, err error) {
	return loader.Load(c.HelmChartPath)
}

func (c *O11yAgentConfig) ToHelmValues() map[string]interface{} {
	return c.HelmValues
}

func (c *O11yAgentConfig) SetHelmValues(values []byte) error {
	return yaml.Unmarshal(values, &c.HelmValues)
}

func (c *O11yAgentConfig) DryRun() bool {
	return false
}

var _ helm.Package = &O11yAgentConfig{}

type RootConfig struct {
	ControllerConfig `json:",inline"`

	ClusterController ClusterControllerConfig `json:"clusterController"`
	BR                BRConfig                `json:"br"`

	Predicate metav1.LabelSelector `json:"predicate"`

	Hibernation Hibernation `json:"hibernation"`

	EnableK8sVersionController bool `json:"enableK8sVersionController"`

	DryRun DryRunConfig `json:"dryRun,omitempty"`
}

type DryRunConfig struct {
	Enabled bool `json:"enabled"`
}

type DumplingConfig struct {
	DumplingImage string `json:"dumplingImage"`
}

func LoadBRConfig(path string) (*BRConfig, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	var config BRConfig
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}
	return &config, nil
}

type BRConfig struct {
	Env                    string               `json:"env"` // used to tag cloud resources
	Provider               string               `json:"provider"`
	Azure                  AzureConfig          `json:"azure,omitempty"`
	InstanceTypeConfigFile string               `json:"instanceTypeConfigFile"`
	Predicate              metav1.LabelSelector `json:"predicate"`
	Region                 string               `json:"region"`
	Zones                  []string             `json:"zones"`
	GCPProjectID           string               `json:"gcpProjectID"`
	Sentry                 Sentry               `json:"sentry"`
	// DPCompatEnv is the env of the global api gateway,enums: dev, staging, prod. defined in https://github.com/tidbcloud/tiinf/blob/master/transport/crossregion/const.go#L9
	DPCompatEnv              string `json:"dpCompatEnv"`
	DPCompatAssumeRole       string `json:"dpCompatAssumeRole"`
	DPCompatAssumeExternalId string `json:"dpCompatAssumeExternalId"`
	DPComaptDataPlaneRoleArn string `json:"dpCompatDataPlaneRoleArn"`
	// NOTE: now only for compat pkg to fetch enabled features of a cluster
	ClusterServiceEndpoint string                          `json:"clusterServiceEndpoint"`
	FeatureGates           map[FeatureGateName]FeatureGate `json:"featureGates"`
	// CRRFDaemon is the Helm chart configuration for the CRRF daemon
	CRRFDaemon *HelmConfig `json:"crrfDaemon,omitempty"`
}

func LoadCDCConfig(path string) (*CDCConfig, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	var config CDCConfig
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}
	return &config, nil
}

type CDCConfig struct {
	Environment           string                          `json:"environment"`
	Predicate             metav1.LabelSelector            `json:"predicate"`
	Region                string                          `json:"region"`
	ConfigServiceEndpoint string                          `json:"configServiceEndpoint"`
	FeatureGates          map[FeatureGateName]FeatureGate `json:"featureGates"`
	Sentry                Sentry                          `json:"sentry"`
	// DPCompatEnv is the env of the global api gateway
	DPCompatEnv              string `json:"dpCompatEnv"`
	DPCompatAssumeRole       string `json:"dpCompatAssumeRole"`
	DPCompatAssumeExternalId string `json:"dpCompatAssumeExternalId"`
	DPComaptDataPlaneRoleArn string `json:"dpCompatDataPlaneRoleArn"`
	// NOTE: now only for compat pkg to fetch enabled features of a cluster
	ClusterServiceEndpoint string `json:"clusterServiceEndpoint"`
	// Override is for gitops override
	Override OverrideConfig `json:"override"`
}

func (c *CDCConfig) GetEnvironment() string {
	if c.Environment != "" {
		return c.Environment
	}
	return "dev"
}

type Hibernation struct {
	Enabled             bool `json:"enabled"`
	cloudv1.Hibernation `json:",inline"`
}

type K8sVersionConfig struct {
	Tenants  map[UintOrString]string `json:"tenants"`
	Projects map[UintOrString]string `json:"projects"`
}

func (cfg *NetworkConfig) GetK8sVersion(tenant, project string) string {
	v, ok := cfg.K8sVersionConfig.Projects[UintOrString(project)]
	if ok {
		return v
	}
	v, ok = cfg.K8sVersionConfig.Tenants[UintOrString(tenant)]
	if ok {
		return v
	}
	return cfg.K8sVersion
}

type WorkerNodeAMIConfig struct {
	SupportedVersions []string `json:"supportedVersions"`

	// will be populated at runtime
	X86AMIIDs map[string]string `json:"x86AMIIDs"`
	ARMAMIIDs map[string]string `json:"armAMIID"`

	X86AMIID *string `json:"x86"`
	ARMAMIID *string `json:"arm"`
}

type ControllerConfig struct {
	// For global resources such as dns, pca, use ControlplaneAWSProviderConfig to provision
	// Currently the config is named as `provider-aws` in all environments
	ControlplaneAWSProviderConfig string `json:"controlplaneAWSProviderConfig"`
	// For GCP global resources such as gcr image, use ControlplaneGCPProviderConfig to provision. We need to grant dataplane project to access controlplane gcr image
	// Currently the config is named as `provider-gcp` in all environments
	ControlplaneGCPProviderConfig string `json:"controlplaneGCPProviderConfig"`
	// For Azure global resources such as azure dns, use ControlplaneAzureProviderConfig to provision.
	// Currently the config is named as `provider-azure` in all environments
	ControlplaneAzureProviderConfig string `json:"controlplaneAzureProviderConfig"`

	Network               NetworkConfig  `json:"network"`
	Override              OverrideConfig `json:"override"`
	ConfigServiceEndpoint string         `json:"configServiceEndpoint"`

	// Some storageclasses will not be used by tidb-mgmt, but will be used by cluster component, such as tidb `index-acceleration`
	// We don't want to hardcode it
	// In the future these might be all passed by tidb-mgmt to support billing, so at the moment we config it here
	// To avoid restarting the cluster-controller when adding a new storageclass, this config file is re-read everytime we need to sync storageclass
	// This is a poor-man's version of hot-reload implementation and might be a performance issue when there are lots of clusters to be created, but should work well for our current scale
	StorageClassConfigFile string `json:"storageClassConfigFile"`

	InstanceTypeConfigFile string `json:"instanceTypeConfigFile"`

	DWWorker DWWorkerConfig `json:"dwWorker"`

	Provider    string     `json:"provider"`
	ServicePlan string     `json:"servicePlan"`
	FullManage  FullManage `json:"fullManage"`

	// We can use SPOT instance for cluster to save cost.
	// Should only be set to save cost in dev.
	CapacityType *string `json:"capacityType,omitempty"` // optional: ON_DEMAND;SPOT

	AMIConfig WorkerNodeAMIConfig `json:"amiConfig"`

	Sentry Sentry `json:"sentry"`

	TaskEvents TaskEvents `json:"taskEvents"`

	AccountScheduler AcctSchedConfig `json:"accountScheduler,omitempty"`

	TiflowCluster TiflowClusterConfig `json:"tiflowCluster"`

	// TiProxyConfig TiProxyConfig `json:"tiproxyConfig"`

	FeatureGates map[FeatureGateName]FeatureGate `json:"featureGates"`

	ExpFeatureGates []ExpFeatureGate `json:"expFeatureGates"`

	// MaxConcurrentReconciles controls the number of concurrent reconciles for each controller.
	// Key: controller name, value: max concurrent reconciles.
	MaxConcurrentReconciles map[string]int `json:"maxConcurrentReconciles"`

	DumplingConfig DumplingConfig `json:"dumplingConfig"`

	// DPCompatEnv is the env of the global api gateway
	DPCompatEnv              string `json:"dpCompatEnv"`
	DPCompatAssumeRole       string `json:"dpCompatAssumeRole"`
	DPCompatAssumeExternalId string `json:"dpCompatAssumeExternalId"`
	DPComaptDataPlaneRoleArn string `json:"dpCompatDataPlaneRoleArn"`

	RegionalServerServiceEndpoint string `json:"regionalServerServiceEndpoint"`
	// TiCDCServiceEndpoint is the HTTP endpoint of the ticdc-service grpc-gateway (e.g. "ticdc-service:8080").
	TiCDCServiceEndpoint string `json:"ticdcServiceEndpoint"`
	// NOTE: now only for compat pkg to fetch enabled features of a cluster
	ClusterServiceEndpoint string `json:"clusterServiceEndpoint"`

	ImportConfig ImportConfig `json:"importConfig"`
	DMConfig     DMConfig     `json:"dmConfig"`
}

func (c *ControllerConfig) GetTiCDCServiceEndpoint() string {
	if c.TiCDCServiceEndpoint != "" {
		return c.TiCDCServiceEndpoint
	}
	return "http://ticdc-service-server.ticdc-service"
}

// NOTE: we use uint64 for tenant and project id previously,
// so we define this type for compatibility
type UintOrString string

func (ns *UintOrString) UnmarshalJSON(val []byte) error {
	if val[0] == '"' {
		var s string
		if err := json.Unmarshal(val, &s); err != nil {
			return err
		}
		*ns = UintOrString(s)
		return nil
	}
	var n uint64
	if err := json.Unmarshal(val, &n); err != nil {
		return err
	}

	*ns = UintOrString(strconv.FormatUint(n, 10))
	return nil
}

type TiflowClusterConfig struct {
	Master   TiflowClusterMasterConfig   `json:"master"`
	Executor TiflowClusterExecutorConfig `json:"executor"`
}

type TiflowClusterExecutorConfig struct {
	ImageConfig ImageConfig `json:"imageConfig"`
}

type TiflowClusterMasterConfig struct {
	DefaultSpecCode  string              `json:"defaultSpecCode"`
	ExpirationMinute int64               `json:"expirationMinute"`
	ShareDb          TiflowShareDBConfig `json:"shareDb"`
	ImageConfig      ImageConfig         `json:"imageConfig"`
}

type TiflowShareDBConfig struct {
	EnableProxy bool   `json:"enableProxy"`
	ProxyImage  string `json:"proxyImage"`
	ServiceName string `json:"serviceName"`
	Port        int    `json:"port"`
	User        string `json:"user"`
	Password    string `json:"password"`
}

type NetworkConfig struct {
	CertManager                CertManagerConfig               `json:"certmanager"`
	CSI                        CSIConfig                       `json:"csi"`
	AWSLoadBalancerController  AWSLoadBalancerControllerConfig `json:"awsLoadBalancerController"`
	Operator                   OperatorConfig                  `json:"operator"`
	Proxy                      ProxyConfig                     `json:"proxy"`
	ClusterGateway             ClusterGatewayConfig            `json:"clusterGateway"`
	DataflowAgent              DataflowAgentConfig             `json:"dataflowAgent"`
	TiflowOperator             TiflowOperatorConfig            `json:"tiflowOperator"`
	PCAIssuer                  PCAIssuerConfig                 `json:"pcaIssuer"`
	SLIReporter                SLIReporterConfig               `json:"sliReporter"`
	EnableEKSPublicAccess      bool                            `json:"enableEksPublicAccess"`
	ApiserverPublicAccessCidrs []string                        `json:"apiserverPublicAccessCidrs"`
	Region                     string                          `json:"region"`
	Zones                      []string                        `json:"zones"`
	O11yConfig                 ObservabilityInfraConfig        `json:"o11yConfig"`
	KubeStateMetrics           ComponentConfig                 `json:"kubeStateMetrics"`
	NodeExporter               ComponentConfig                 `json:"nodeExporter"`
	ClusterAutoscaler          ComponentConfig                 `json:"clusterAutoscaler"`
	K8sVersion                 string                          `json:"k8sVersion"`
	K8sVersionConfig           K8sVersionConfig                `json:"k8sVersionConfig"`
	IgnoreDelete               []string                        `json:"ignoreDelete"`

	ElasticHTAP             ElasticHTAPConfig             `json:"elasticHTAP"`
	Calico                  CalicoConfig                  `json:"calico"`
	NetworkPolicyController NetworkPolicyControllerConfig `json:"networkPolicyController"`
	PodIdentityWebhook      PodIdentityWebhookConfig      `json:"podIdentityWebhook"`

	GKEMaintenanceExclusionTimeWindow GKEMaintenanceExclusionTimeWindow `json:"gkeMaintenanceExclusionTimeWindow,omitempty"`
	AdminNodesAMIType                 string                            `json:"adminNodesAMIType,omitempty"`

	SelfSignedCAConfig SelfSignedCAConfig `json:"selfSignedCAConfig"`
}

// OverrideConfig is the configurations for image overriding.
type OverrideConfig struct {
	// OverrideHelperImage is the full path for busybox helper image
	OverrideHelperImage string `json:"overrideHelperImage"`
	// OverrideRegistry is the registry domain name, e.g. ***.dkr.ecr.us-west-2.amazonaws.com
	OverrideRegistry string `json:"overrideRegistry"`
	// OverrideRepoPrefix is the image prefix path
	OverrideRepoPrefix string `json:"overrideRepoPrefix"`
	// OverrideThirdPartyRepoPrefix is the image prefix path for third party images
	OverrideThirdPartyRepoPrefix string `json:"overrideThirdPartyRepoPrefix"`
}

type ObservabilityInfraConfig struct {
	cloudv1.ObservabilityInfraConfig `json:",inline"`
	MetricsAgentConfig               O11yAgentConfig `json:"metricsAgent"`
	ClusterAgentConfig               O11yAgentConfig `json:"clusterAgent"`
	LogAgentConfig                   O11yAgentConfig `json:"logAgent"`
	LokiConfig                       O11yAgentConfig `json:"loki"`
	So11yConfig                      O11yAgentConfig `json:"so11y"`
	EventExporterConfig              O11yAgentConfig `json:"eventExporter"`
	InfraTenantID                    string          `json:"infraTenantID"`
	// If set true, will not deploy log agent to EKS.
	DisableDoubleWriteLogs bool   `json:"disableDoubleWriteLogs"`
	LokiServerDns          string `json:"lokiServerDns"`
	MaxWaitSeconds         int    `json:"maxWaitSeconds"`
}

type Sentry struct {
	DSN         string `json:"dsn"`
	Environment string `json:"env"`
}

type TaskEvents struct {
	ProjectID string `json:"projectID"`
	TopicID   string `json:"topicID"`
}

type AuditConfig struct {
	SidecarImage string `json:"sidecarImage,omitempty"`
	// SidecarImageV2 is defined for clusters created by tidb-operator v2.
	// Unlike SidecarImage, It can be modified.
	// If it's not set, fallback to SidecarImage.
	SidecarImageV2 string   `json:"sidecarImageV2,omitempty"`
	SidecarCommand []string `json:"sidecarCommand,omitempty"`

	BaseImage         string      `json:"baseImage"`
	DefaultTag        string      `json:"defaultTag"`
	DefaultTagFeature FeatureGate `json:"defaultTagFeature"`
}

type DWWorkerConfig struct {
	Image string `json:"image,omitempty"`
}

type ImportConfig struct {
	InstanceType string      `json:"instanceType"`
	CpuCore      int         `json:"cpuCore"`
	Azure        AzureConfig `json:"azure,omitempty"`
	// NormalPodAliveMinutes is the minutes of the normal pod alive after the job is completed
	NormalPodAliveMinutes int `json:"normalPodAliveMinutes"`
	// UnnormalPodAliveMinutes is the minutes of the unnormal pod alive after the job failed
	UnnormalPodAliveMinutes int `json:"unnormalPodAliveMinutes"`
}

type DMConfig struct {
	Azure  AzureConfig      `json:"azure,omitempty"`
	AWS    AWSConfig        `json:"aws,omitempty"`
	Master DMInstanceConfig `json:"masterInstance"`
	Worker DMInstanceConfig `json:"workerInstance"`
}

type DMInstanceConfig struct {
	ImageConfig `json:",inline"`
	SpecCode    string `json:"specCode"`
}

type FullManage struct {
	Project                string `json:"project"`
	PublicHostedZoneDomain string `json:"publicHostedZoneDomain"`
	PublicHostedZoneID     string `json:"publicHostedZoneID"`

	account.Info                       `json:",inline"`
	GCPProjectID                       string `json:"gcpProjectID"`
	AzurePublicHostedResourceGroupName string `json:"azurePublicHostedResourceGroupName"`
}

type CertManagerConfig struct {
	HelmConfig `json:",inline"`

	CAInjectorImage ImageConfig `json:"cainjectorImage"`
	WebhookImage    ImageConfig `json:"webhookImage"`
	ControllerImage ImageConfig `json:"controllerImage"`
}

type CSIConfig struct {
	HelmConfig `json:",inline"`

	Driver              ImageConfig `json:"driver"`
	Provisioner         ImageConfig `json:"provisioner"`
	Attacher            ImageConfig `json:"attacher"`
	LivenessProbe       ImageConfig `json:"livenessProbe"`
	Resizer             ImageConfig `json:"resizer"`
	NodeDriverRegistrar ImageConfig `json:"nodeDriverRegistrar"`
}

type AWSLoadBalancerControllerConfig struct {
	HelmConfig `json:",inline"`

	ControllerImage ImageConfig `json:"controllerImage"`
}

type OperatorConfig struct {
	HelmConfig `json:",inline"`

	Operator            ImageConfig `json:"operator"`
	TidbBackupManager   ImageConfig `json:"tidbBackupManager"`
	AdvancedStatefulset ImageConfig `json:"advancedStatefulset"`
	KubeScheduler       ImageConfig `json:"kubeScheduler"`
	DBMonitorProm       ImageConfig `json:"dbMonitorProm"`
	DBMonitorGrafana    ImageConfig `json:"dbMonitorGrafana"`
}

type Resources struct {
	CPURequests    string `json:"cpuRequests"`
	CPULimits      string `json:"cpuLimits"`
	MemoryRequests string `json:"memoryRequests"`
	MemoryLimits   string `json:"memoryLimits"`
}

type ProxyConfig struct {
	HelmConfig    `json:",inline"`
	ProxyImage    ImageConfig `json:"proxyImage"`
	GottyImage    ImageConfig `json:"gottyImage"`
	AllowedCidrs  []string    `json:"allowedCidrs"`
	Auth0Audience string      `json:"auth0Audience"`
	Auth0Domain   string      `json:"auth0Domain"`
	Resources     Resources   `json:"resources"`
	EnableDevMode bool        `json:"enableDevMode"`
}

type ClusterGatewayConfig struct {
	HelmConfig                 `json:",inline"`
	AllowedCidrs               []string    `json:"allowedCidrs"`
	Resources                  Resources   `json:"resources"`
	KongImage                  ImageConfig `json:"kongImage"`
	KongIngressControllerImage ImageConfig `json:"kongIngressControllerImage"`
}

type ClusterAgentConfig struct {
	HelmConfig              `json:",inline"`
	AgentImage              ImageConfig       `json:"agentImage"`
	Resources               Resources         `json:"resources"`
	PDPatrolImage           PatrolImageConfig `json:"pdPatrolImage"`
	AutoUnlockPending       bool              `json:"autoUnlockPending"`
	AutoUnlockMinPendingAge metav1.Duration   `json:"autoUnlockMinPendingAge"`
}

type DataflowAgentConfig struct {
	HelmConfig         `json:",inline"`
	AllowedCidrs       []string    `json:"allowedCidrs"`
	DataflowAgentImage ImageConfig `json:"dataflowAgentImage"`
}

type TiflowOperatorConfig struct {
	HelmConfig          `json:",inline"`
	TiflowOperatorImage ImageConfig `json:"tiflowOperatorImage"`
	Enable              bool        `json:"enable"`
}

type ElasticHTAPConfig struct {
	Namespace         string                  `json:"namespace"`
	TiFlashAutoscaler TiFlashAutoscalerConfig `json:"tiflashAutoscaler"`
	Prometheus        PrometheusConfig        `json:"prometheus"`
	OpenKruise        OpenKruiseConfig        `json:"openKruise"`
}

type TiFlashAutoscalerConfig struct {
	Image           ImageConfig `json:"image"`
	SupervisorImage ImageConfig `json:"supervisorImage"`
}

type PrometheusConfig struct {
	Image ImageConfig `json:"image"`
}

type OpenKruiseConfig struct {
	HelmConfig   `json:",inline"`
	ManagerImage ImageConfig `json:"managerImage"`
}

type CalicoConfig struct {
	HelmConfig `json:",inline"`
	Registry   string `json:"registry"`
}

type NetworkPolicyControllerConfig struct {
	Image ImageConfig `json:"image"`
}

type PodIdentityWebhookConfig struct {
	Image ImageConfig `json:"image"`
}

type GKEMaintenanceExclusionTimeWindow struct {
	StartTime *time.Time `json:"startTime"`
	EndTime   *time.Time `json:"endTime"`
}

type PCAIssuerConfig struct {
	HelmConfig `json:",inline"`
	Image      ImageConfig `json:"image"`
}

type SLIReporterConfig struct {
	HelmConfig            `json:",inline"`
	Image                 ImageConfig `json:"image"`
	AWSRegion             string      `json:"awsRegion"`
	DeliveryStreamName    string      `json:"deliveryStreamName"`
	DeliveryStreamRoleArn string      `json:"deliveryStreamRoleArn"`
}

type HelmConfig struct {
	ChartPath string   `json:"chartPath"`
	CRDPaths  []string `json:"crdPaths"`
}

// ComponentConfig is a reusable config for components.
type ComponentConfig struct {
	HelmConfig `json:",inline"`
	Image      ImageConfig `json:"image"`
}

func LoadRootConfig(path string) (RootConfig, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return RootConfig{}, err
	}
	var config RootConfig
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		return RootConfig{}, err
	}

	if config.CapacityType != nil && *config.CapacityType == "" {
		config.CapacityType = nil
	}

	return config, nil
}

func (r *RootConfig) GenerateLabelFromConfig() map[string]string {
	labels := map[string]string{}
	r.fillLabelFromMatchLabels(labels)
	r.fillLabelFromMatchExpressions(labels)
	return labels
}

func (r *RootConfig) fillLabelFromMatchExpressions(labels map[string]string) {
	matchExpressions := r.Predicate.MatchExpressions
	if len(matchExpressions) <= 0 {
		return
	}
	for _, requirement := range matchExpressions {
		if requirement.Operator == metav1.LabelSelectorOpIn && len(requirement.Values) > 0 {
			labels[requirement.Key] = requirement.Values[0]
		}
	}
}

func (r *RootConfig) fillLabelFromMatchLabels(labels map[string]string) {
	matchLabels := r.Predicate.MatchLabels
	if len(matchLabels) <= 0 {
		return
	}
	for key, value := range matchLabels {
		labels[key] = value
	}
}

func (r *RootConfig) EnabledNetworkHibernation(tenant, project string) bool {
	return FeatureEnabled(r.FeatureGates, NetworkHibernation, tenant, project)
}

func (r *RootConfig) FindRegionZones(region string) ([]string, error) {
	if region != r.Network.Region {
		return nil, fmt.Errorf("region mismatch")
	}
	zones := r.Network.Zones
	if len(zones) != 3 {
		return nil, fmt.Errorf("zones misconfigured")
	}
	// make sure the zones are sorted, else might trigger some cluster resource update, such as node pool.
	sort.Strings(zones)
	return zones, nil
}

var _ CloudConfigProvider = &RootConfig{}

func (c *ControllerConfig) GetProviderType() string {
	return c.Provider
}

func (c *ControllerConfig) GetInstanceTypes() ([]cloudv1.InstanceType, error) {
	data, err := os.ReadFile(c.InstanceTypeConfigFile)
	if err != nil {
		return nil, err
	}
	var instanceTypes []cloudv1.InstanceType
	err = yaml.Unmarshal(data, &instanceTypes)
	if err != nil {
		return nil, err
	}
	return instanceTypes, nil
}

func (c *ControllerConfig) FindInstanceType(name string) (cloudv1.InstanceType, error) {
	instanceTypes, err := c.GetInstanceTypes()
	if err != nil {
		return cloudv1.InstanceType{}, err
	}
	for _, t := range instanceTypes {
		if t.Name == name {
			return t, nil
		}
	}
	return cloudv1.InstanceType{}, fmt.Errorf("instance type %q not found", name)
}

func (c *ControllerConfig) GetStorageClasses() ([]*storagev1.StorageClass, error) {
	path := c.StorageClassConfigFile
	return parseStorageClassFromConfigFile(path)
}
