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"
	ProviderAliCloud = "alicloud"
)

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"`
	// Zones is the list of zones in the region
	Zones []string `toml:"zones" json:"zones"`
	// TiCDCPreStopImage is the image used to stop TiCDC gracefully when using TiDB Operator v2
	TiCDCPreStopImage string `toml:"ticdcPreStopImage" json:"ticdcPreStopImage" default:""`
	// TiCDCBaseImage is the base image of ticdc cluster,
	// like: 380838443567.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/ticdc
	// the controller will ignore the baseImage specified by global
	TiCDCBaseImage string `toml:"ticdcBaseImage" json:"ticdcBaseImage"`

	// 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"`

	// for regional cluster service client
	ClusterServiceBaseURL string `toml:"clusterServiceBaseURL" json:"clusterServiceBaseURL" default:"http://cluster-ng-server.cluster-ng-service"`
	// for regional dataplane service client
	DataPlaneManagerServiceBaseURL string `toml:"dataPlaneManagerServiceBaseURL" json:"dataPlaneManagerServiceBaseURL" default:"http://dataplane-manager.dataplane-manager"`
	// 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"`

	// EnableClusterAgentSQL enables the cluster-agent path for VerifyTable SQL queries.
	// When false (default), uses the legacy clusterproxy SQL path.
	EnableClusterAgentSQL bool `toml:"enableClusterAgentSQL" json:"enableClusterAgentSQL" default:"false"`
}

type DebugConfig struct {
	Enable                     bool   `toml:"enable"  json:"enable" default:"false"`
	EnableRegionalPortForward  bool   `toml:"enableRegionalPortForward" json:"enableRegionalPortForward" default:"false"`
	EnableDataPlanePortForward bool   `toml:"enableDataPlanePortForward" json:"enableDataPlanePortForward" default:"false"`
	DataPlaneKubeConfig        string `toml:"dataPlaneKubeConfig"  json:"dataPlaneKubeConfig" default:""`
}

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"`
}

func LoadAndValidateConfig(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))
	if err = config.Validate(); err != nil {
		return nil, errors.Annotate(err, "failed to validate config file")
	}
	return config, nil
}

func (c *Config) Validate() error {
	if c.TiCDCBaseImage == "" {
		return errors.New("TiCDC base image is required")
	}
	if c.TiCDCPreStopImage == "" {
		return errors.New("TiCDC pre-stop image is required")
	}
	return nil
}
