package config

import (
	"os"

	"github.com/creasty/defaults"
	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"go.uber.org/zap"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/yaml"
)

const (
	ProviderAzure = "azure"
	ProviderAWS   = "aws"
	ProviderGCP   = "gcp"
)

type FeatureName string

const FeatureTiCDCCertificate = "ticdc-certificate"

type Config struct {
	// LogLevel is the log level.
	LogLevel string `toml:"log-level" json:"log-level" default:"info"`
	// Addr is the address to listen on.
	Addr string `toml:"addr" json:"addr" default:":8080"`
	// InternalGRPCAddr is the address of internal grpc server.
	InternalGRPCAddr string `toml:"internal-grpc-addr" json:"internal-grpc-addr" default:":8280"`
	// Kubeconfig is the path to Kubeconfig.
	Kubeconfig string `toml:"kubeconfig" json:"kubeconfig"`
	// Namespace is the namespace prefix of objects managed by cluster-server
	Namespace string `toml:"namespace" json:"namespace" default:"ticdc-service"`
	// Provider is the cloud provider.
	Provider string `toml:"provider" json:"provider"`
	// Region is the region label of objects managed by cluster-controller
	Region string `toml:"region" json:"region"`
	// GCConfig is the config for cloud resource gc
	GCConfig GCConfig `toml:"gc-config" json:"gc-config"`

	// ClusterNSPrefix is the tidb Cluster CR namespace prefix
	// We use nightly-ms, staging, prod in different environment
	ClusterNSPrefix string `toml:"clusterNSPrefix" json:"clusterNSPrefix" default:"nightly-ms"`

	// TiCDCPreStopImage is the image used to stop TiCDC gracefully when using TiDB Operator v2
	TiCDCPreStopImage     string `toml:"ticdcPreStopImage" json:"ticdcPreStopImage" default:""`
	TiCDCCertSidecarImage string `toml:"ticdcCertSidecarImage" json:"ticdcCertSidecarImage" default:""`

	// Cloud provider specific config
	Azure *AzureConfig `toml:"azure" json:"azure"`
	AWS   *AwsConfig   `toml:"aws" json:"aws"`

	// for controller-runtime
	EnableLeaderElection bool                 `toml:"enableLeaderElection" json:"enableLeaderElection"`
	ProbeAddr            string               `toml:"probeAddr" json:"probeAddr" default:":8381"`
	MetricsAddr          string               `toml:"metricsAddr" json:"metricsAddr" default:":8380"`
	Predicate            metav1.LabelSelector `toml:"predicate" json:"predicate"`

	// clusterproxy auth config
	Proxy ProxyConfig `toml:"proxy" json:"proxy"`
	// for regional service client
	RegionalServiceBaseURL string `toml:"regionalServiceBaseURL" json:"regionalServiceBaseURL" default:"http://regional-server.regional-server-system"`
	// for debug
	Debug DebugConfig `toml:"debug" json:"debug"`
	// Env is the environment controller running
	// It's used to tag cloud resources such as nodepool
	Env string `toml:"env" json:"env"`

	SimpleFeatureGates map[FeatureName]bool `toml:"simpleFeatureGates" json:"simpleFeatureGates"`
}

// IsFeatureEnabled checks if a feature is enabled in the feature gates
func (c *Config) IsFeatureEnabled(featureName FeatureName) bool {
	if len(c.SimpleFeatureGates) > 0 {
		return c.SimpleFeatureGates[featureName]
	}
	return false
}

type DebugConfig struct {
	Enable              bool   `toml:"enable"  json:"enable" default:"false"`
	ClusterProxy        string `toml:"clusterProxy"  json:"clusterProxy" default:""`
	DataPlaneKubeConfig string `toml:"dataPlaneKubeConfig"  json:"dataPlaneKubeConfig" default:""`
}

type ProxyConfig struct {
	EnableDevMode bool   `json:"enableDevMode"`
	Auth0Audience string `json:"auth0Audience"`
	Auth0Domain   string `json:"auth0Domain"`
}

type AzureConfig struct {
	// Crossplane Managed Identity Configurations for crossplane providerconfig construction
	CrossplaneProviderTenantID string `toml:"crossplaneProviderTenantID" json:"crossplaneProviderTenantID"`
	CrossplaneProviderClientID string `toml:"crossplaneProviderClientID" json:"crossplaneProviderClientID"`
}

type AwsConfig struct {
	AssumeRoleName string `json:"assumeRoleName"`
	ExternalID     string `json:"externalID"`
}

type GCConfig struct {
	// CheckIntervalInSecond is the interval of gc round
	CheckIntervalInSecond int `json:"checkIdleInterval" default:"300"`
}

func LoadConfig(path string) (*Config, error) {
	data, err := os.ReadFile(path)
	log.Info("loading config file", zap.String("path", path))
	if err != nil {
		return nil, errors.Annotate(err, "failed to read config file")
	}
	var config = &Config{}
	if err = defaults.Set(config); err != nil {
		return nil, errors.Annotate(err, "failed to set default config")
	}
	if err = yaml.Unmarshal(data, config); err != nil {
		return nil, errors.Annotate(err, "failed to unmarshal config file")
	}
	log.Info("loading config file", zap.Any("Config", *config))
	return config, nil
}
