package config

import (
	"os"
	"time"

	"gopkg.in/yaml.v3"
)

const (
	ProviderAWS                      = "aws"
	ProviderGCP                      = "gcp"
	ProviderAzure                    = "azure"
	ProviderAliCloud                 = "alicloud"
	DefaultBucket                    = "o11y-dev-shared-us-west-2"
	DefaultRegion                    = "us-west-2"
	DefaultTaskFilePrefix            = "drhouse/diag-task"
	DefaultClusterMetaPrefix         = "drhouse/cluster-meta"
	DefaultTaskSyncInterval          = 15 * time.Minute
	DefaultCatalogPath               = "drhouse/diag-catalog/catalog.yaml"
	DefaultServerlessVIPListPath     = "drhouse/cluster-meta/serverless-vip-list.csv"
	DefaultServerlessSpendLimitsPath = "drhouse/cluster-meta/serverless-spend-limits.json"
	DefaultServerlessSkuPricePath    = "drhouse/cluster-meta/serverless-sku-price.json"
	DefaultDiagEngineSyncInterval    = 5 * time.Minute
	DefaultDiagEngineNamespace       = "drhouse"
	DefaultDiagEngineName            = "diagnosis"
	DefaultMaxTasksPerEngine         = 20
	DefaultMinTasksPerEngine         = 5
)

// DiagConfig defines the config of diag controller
type DiagConfig struct {
	// Enable controls if enable diag service
	Enable bool `json:"enable" yaml:"enable"`
	// Task defines the config of diag task
	Task TaskConfig `json:"task" yaml:"task"`
	// DiagEngine defines the config of diag-engine
	DiagEngine DiagEngineConfig `json:"diagEngine" yaml:"diagEngine"`
	// Provider defines the cloud provider of the cluster, default is aws
	Provider string `json:"provider" yaml:"provider"`
	// Region defines the region of the cluster
	Region string `json:"region" yaml:"region"`
	// Project defines the project of the cluster
	Project string `json:"project" yaml:"project"`
}

func (d *DiagConfig) setDefaultConfig() {
	if d.Provider == "" {
		d.Provider = ProviderAWS
	}
	if d.Region == "" {
		d.Region = DefaultRegion
	}
	d.Task.setDefaultConfig()
	d.DiagEngine.setDefaultConfig()
}

type TaskConfig struct {
	// Interval defines the interval of diag controller to distribute diag tasks
	Interval time.Duration `json:"interval" yaml:"interval"`
	// StorageAccount defines the storage account of the azure blob storage
	StorageAccount string `json:"storageAccount" yaml:"storageAccount"`
	// Bucket defines the bucket to store diag tasks files and catalog files
	Bucket string `json:"bucket" yaml:"bucket"`
	// TaskFilePrefix defines the prefix of diag tasks files
	TaskFilePrefix string `json:"taskFilePrefix" yaml:"taskFilePrefix"`
	// CatalogPath defines the path of catalog files
	CatalogPath string `json:"catalogPath" yaml:"cataLogPath"`
	// ClusterMetaFilePrefix defines the prefix of cluster meta files
	ClusterMetaFilePrefix string `json:"clusterMetaFilePrefix" yaml:"clusterMetaFilePrefix"`
	// ServerlessVIPListPath defines the path of serverless vip list
	ServerlessVIPListPath string `json:"serverlessVIPListPath" yaml:"serverlessVIPListPath"`
	// ServerlessSpendLimitsPath defines the path of serverless spend limits
	ServerlessSpendLimitsPath string `json:"serverlessSpendLimitsPath" yaml:"serverlessSpendLimitsPath"`
	// ServerlessSkuPricePath defines the path of serverless ru price
	ServerlessSkuPricePath string `json:"serverlessSkuPricePath" yaml:"serverlessSkuPricePath"`
	// EnableServerless defines if enable serverless cluster diag
	EnableServerless bool `json:"enableServerless" yaml:"enableServerless"`
	// EnableDedicated defines if enable dedicated cluster diag
	EnableDedicated bool `json:"enableDedicated" yaml:"enableDedicated"`
}

func (t *TaskConfig) setDefaultConfig() {
	if t.Interval == 0 {
		t.Interval = DefaultTaskSyncInterval
	}
	if t.Bucket == "" {
		t.Bucket = DefaultBucket
	}
	if t.TaskFilePrefix == "" {
		t.TaskFilePrefix = DefaultTaskFilePrefix
	}
	if t.ClusterMetaFilePrefix == "" {
		t.ClusterMetaFilePrefix = DefaultClusterMetaPrefix
	}
	if t.CatalogPath == "" {
		t.CatalogPath = DefaultCatalogPath
	}
	if t.ServerlessVIPListPath == "" {
		t.ServerlessVIPListPath = DefaultServerlessVIPListPath
	}
	if t.ServerlessSpendLimitsPath == "" {
		t.ServerlessSpendLimitsPath = DefaultServerlessSpendLimitsPath
	}
	if t.ServerlessSkuPricePath == "" {
		t.ServerlessSkuPricePath = DefaultServerlessSkuPricePath
	}
}

type DiagEngineConfig struct {
	// Interval defines the interval of to check and scan diag-engine StatefulSet
	Interval time.Duration `json:"interval" yaml:"interval"`
	// Namespace defines the namespace of diag-engine StatefulSet
	Namespace string `json:"namespace" yaml:"namespace"`
	// Name defines the name of diag-engine StatefulSet
	Name string `json:"name" yaml:"name"`
	// MaxTasksPerEngine defines the max tasks count of per diag-engine,
	// if the number of tasks is greater than MaxTasksPerEngine, controller will scale up diag-engine StatefulSet
	MaxTasksPerEngine int `json:"maxTasksPerEngine" yaml:"maxTasksPerEngine"`
	// MinTasksPerEngine defines the min tasks of per diag-engine,
	// if the number of tasks is less than MinTasksPerEngine, controller will scale down diag-engine StatefulSet
	MinTasksPerEngine int `json:"minTasksPerEngine" yaml:"minTasksPerEngine"`
}

type ResourceRequirements struct {
	Requests ResourcePair `json:"requests" yaml:"requests"`
	Limits   ResourcePair `json:"limits" yaml:"limits"`
}

type ResourcePair struct {
	CPU    string `json:"cpu" yaml:"cpu"`
	Memory string `json:"memory" yaml:"memory"`
}

func (d *DiagEngineConfig) setDefaultConfig() {
	if d.Interval == 0 {
		d.Interval = DefaultDiagEngineSyncInterval
	}
	if d.Namespace == "" {
		d.Namespace = DefaultDiagEngineNamespace
	}
	if d.Name == "" {
		d.Name = DefaultDiagEngineName
	}
	if d.MaxTasksPerEngine == 0 {
		d.MaxTasksPerEngine = DefaultMaxTasksPerEngine
	}
	if d.MinTasksPerEngine == 0 {
		d.MinTasksPerEngine = DefaultMinTasksPerEngine
	}
}

// LoadFromPath loads config from path.
// If config file not exist, return enpty config and the enable field is false.
func LoadFromPath(path string) *DiagConfig {
	conf := &DiagConfig{}
	_, err := os.Stat(path)
	if os.IsNotExist(err) {
		return conf
	}
	in, err := os.ReadFile(path)
	if err != nil {
		panic(err)
	}
	err = yaml.Unmarshal(in, &conf)
	if err != nil {
		panic(err)
	}
	conf.setDefaultConfig()
	return conf
}
