package db

import (
	"fmt"
	"slices"
	"time"

	"github.com/go-sql-driver/mysql"
	"gopkg.in/yaml.v3"
)

type Config struct {
	User     string `yaml:"user"`
	Host     string `yaml:"host"`
	Database string `yaml:"database"`
	// InitDatabase is used to connect to the very first created schema.
	InitDatabase       string        `yaml:"init-database"`
	Password           string        `yaml:"password"`
	Port               uint16        `yaml:"port"`
	ConnMaxLifetime    time.Duration `yaml:"conn-max-lifetime"`
	MaxIdleConnections int           `yaml:"max-idle-connections"`
	MaxOpenConnections int           `yaml:"max-open-connections"`
	TLSConfigKey       string        `yaml:"tls-config-key"`
}

func (c *Config) DSN() string {
	return c.ToMySQLConfigWithDefaults().FormatDSN()
}

// DSNWithInitDB is used to connect to the very first created schema.
func (c *Config) DSNWithInitDB() string {
	localConfig := c.ToMySQLConfigWithDefaults()

	initDatabase := c.InitDatabase
	localConfig.DBName = initDatabase
	return localConfig.FormatDSN()
}

func (c *Config) 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.Loc = time.UTC
	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.AllowNativePasswords = true

	// Only allow TLS config key to be one of the following values.
	// Currently, not support customized server CA or enable mTLS (server verify client).
	if !slices.Contains([]string{"", "true", "false", "skip-verify", "preferred"}, c.TLSConfigKey) {
		panic(
			fmt.Sprintf(
				"tls mod not supported: %s, refer to https://github.com/go-sql-driver/mysql#tls",
				c.TLSConfigKey,
			),
		)
	}
	defaultConfig.TLSConfig = c.TLSConfigKey

	return defaultConfig
}

func (c *Config) LogYamlWithoutPwd() string {
	pwd := c.Password
	c.Password = "******"
	s, err := yaml.Marshal(c) //nolint:gosec // G117: Password field is already masked above
	if err != nil {
		return ""
	}
	c.Password = pwd
	return string(s)
}

func (c *Config) LogDSNWithoutPwd() string {
	mc := c.ToMySQLConfigWithDefaults()
	mc.Passwd = "******"
	return mc.FormatDSN()
}
