package db

import (
	"strings"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/tidbcloud/ecosystem-service/config"
	"github.com/tidbcloud/pkgv2/gormutil"
	"github.com/tidbcloud/tiinf/storage/tigorm"
	"go.uber.org/atomic"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// initialized prevents the database connection pool being initialized multiple
// times. An error will be returned to the caller if the database has been initialized.
var initialized atomic.Bool

// InitializeDB initializes the global database connection pool and return error
// if some unexpected errors from the driver and gorm package raised.
func InitializeDB(conf *config.ConfigWrapper) (*gorm.DB, error) {
	if initialized.Swap(true) {
		return nil, nil
	}

	connCfg := gormutil.Config{
		User:              conf.RdsConfig.User,
		Host:              conf.RdsConfig.Host,
		Database:          conf.RdsConfig.Database,
		Password:          conf.RdsConfig.Password,
		Port:              conf.RdsConfig.Port,
		EnableDetailedLog: conf.ServerConfig.EnableDBQueryLog,
		LogLevel:          conf.ServerConfig.LogLevel,
	}

	var logLevel logger.LogLevel
	switch strings.ToLower(connCfg.LogLevel) {
	case "fatal":
		logLevel = logger.Silent
	case "error":
		logLevel = logger.Error
	case "warn":
		logLevel = logger.Warn
	default:
		logLevel = logger.Info
	}
	cfg := &gorm.Config{
		Logger: tigorm.NewLoggerAdapter(connCfg.EnableDetailedLog, logLevel),
	}

	db, err := tigorm.InitDB(mysql.Open(connCfg.DSN()), cfg)
	if err != nil {
		return nil, errors.Annotate(err, connCfg.DSNWithoutPassword())
	}
	sqlDB, err := db.DB()
	if err != nil {
		return nil, errors.Annotate(err, connCfg.DSNWithoutPassword())
	}

	if connCfg.ConnMaxLifetime == 0 {
		connCfg.ConnMaxLifetime = time.Minute * 3
	}
	if connCfg.MaxIdleConns == 0 {
		connCfg.MaxIdleConns = 100
	}
	if connCfg.MaxOpenConns == 0 {
		connCfg.MaxOpenConns = 100
	}
	sqlDB.SetConnMaxLifetime(connCfg.ConnMaxLifetime)
	sqlDB.SetMaxIdleConns(connCfg.MaxIdleConns)
	sqlDB.SetMaxOpenConns(connCfg.MaxOpenConns)

	err = db.Use(&MetricsPlugin{})
	if err != nil {
		return nil, errors.Trace(err)
	}
	log.Info("Initialize global databases successfully")

	return db, nil
}
