package config

import (
	"fmt"
	"time"

	"github.com/go-sql-driver/mysql"
)

var globalConf *ServerConfig = &ServerConfig{}

type GrpcConfig struct {
	CoreSvcAddr string `mapstructure:"coreSvcAddr" default:"core-server-grpc:8888"`
}

type ServerConfig struct {
	GrpcConfig        GrpcConfig        `mapstructure:"grpcConfig"`
	DebugPortalConfig DebugPortalConfig `mapstructure:"debugPortal"`
	SessionConfig     SessionConfig     `mapstructure:"session"`
	GrafanaConfig     GrafanaConfig     `mapstructure:"grafana"`
	MysqlConfig       MysqlConfig       `mapstructure:"mysql"`

	SecretStore *SecretConfig `mapstructure:"secret"`
}

type SecretConfig struct {
	SecretKey string `mapstructure:"secretKey"`
	Region    string `mapstructure:"region"`
}

// InfraAPIConfig is the configration items for infra API operations.
type InfraAPIConfig struct {
	// kubeconfig file for the infra API server, ignore this to use the default secret
	KubeConfigPath string `mapstructure:"kubeConfigPath"`

	// ref https://github.com/tidbcloud/infra-api/pull/79
	// Enable this too keep the 'key' of resources do not change in infra-api.
	// will use projectID@dbaas as projectID@infra-api
	ProjectIDAsInfraProjectID bool `mapstructure:"projectIDAsInfraProjectID"`
}

// NeedInit returns true if we need to initialize the infra API.
func (ic *InfraAPIConfig) NeedInit() bool {
	return true
}

type DebugPortalConfig struct {
	OAuthClientID         string `mapstructure:"oAuthClientID" required:"true"`
	OAuthClientSecret     string `mapstructure:"oAuthClientSecret" required:"true"`
	OAuthScopes           string `mapstructure:"oAuthScopes" required:"true"`
	OAuthAuthorizationURL string `mapstructure:"oAuthAuthorizationURL" required:"true"`
	OAuthTokenURL         string `mapstructure:"oAuthTokenURL" required:"true"`
	OAuthUserInfoURL      string `mapstructure:"oAuthUserInfoURL" required:"true"`
	OAuthStateSecret      string `mapstructure:"oAuthStateSecret" required:"true"`
	SignOutURL            string `mapstructure:"signOutURL" required:"true"`
	DisableAuthN          bool   `mapstructure:"disableAuthN" required:"true"`
	//SlowQueryDB           MysqlConfig `mapstructure:"slowQueryDB" required:"true"`
	//TopSqlTidbConfig      MysqlConfig `mapstructure:"topSQLTiDB" required:"true"`
}

type SessionConfig struct {
	// A secret to encrypt or derive sessions.
	// Currently only Debug Portal implements session.
	Secret string `mapstructure:"secret" validate:"required"`
}

type GrafanaConfig struct {
	Server           string `mapstructure:"server"  default:"https://grafana.oauth.dev.shared.aws.tidbcloud.com/"`
	APIKey           string `mapstructure:"apiKey"`
	TidbDashboardTag string `mapstructure:"tidbDashboardTag" default:"tidb-mixin"`
}

type MysqlConfig struct {
	Host     string `mapstructure:"host" default:"127.0.0.1"`
	User     string `mapstructure:"user" default:"root"`
	Port     uint16 `mapstructure:"port" default:"4000"`
	Password string `mapstructure:"password" required:"true" default:"12345678"`
	Database string `mapstructure:"database" default:"dbaas"`
}

// Config represents the configuration which can be converted to a connection string
type ConnConfig struct {
	User              string `valid:"required"`
	Host              string `valid:"required"`
	Database          string `valid:"required"`
	Password          string
	Port              uint16
	EnableDetailedLog bool
	LogLevel          string
	CACert            string
	ClientCert        string
	ClientKey         string
	TLSConfigKey      string
	ConnMaxLifetime   time.Duration
	MaxIdleConns      int
	MaxOpenConns      int
}

// DSN gives a connection string suitable to go mysql driver
func (c ConnConfig) DSN() string {
	return c.toMySQLConfigWithDefaults().FormatDSN()
}

func (c ConnConfig) toMySQLConfigWithDefaults() *mysql.Config {
	defaultConfig := mysql.NewConfig()

	defaultConfig.Net = "tcp"
	defaultConfig.User = c.User
	defaultConfig.Addr = fmt.Sprintf("%s:%d", c.Host, c.Port)
	defaultConfig.Passwd = c.Password
	defaultConfig.DBName = c.Database
	defaultConfig.ParseTime = true
	defaultConfig.Timeout = 30 * time.Second
	defaultConfig.ReadTimeout = 30 * time.Second
	defaultConfig.WriteTimeout = 30 * time.Second
	defaultConfig.Params = map[string]string{"charset": "utf8mb4"}
	defaultConfig.Collation = "utf8mb4_bin"
	defaultConfig.TLSConfig = c.TLSConfigKey

	return defaultConfig
}

// DSNWithoutPassword implements the fmt.Stringer interface.
func (c ConnConfig) DSNWithoutPassword() string {
	mysqlConfig := c.toMySQLConfigWithDefaults()
	mysqlConfig.Passwd = ""
	return mysqlConfig.FormatDSN()
}

func (c ConnConfig) DSNWithTestDB() string {
	localConfig := c.toMySQLConfigWithDefaults()
	// Fixme: Is forcing "test" database necessary when init db driver?
	localConfig.DBName = "test"
	return localConfig.FormatDSN()
}
