package db

import (
	"fmt"
	"github.com/pingcap/dbaas/cost-metering-service/config"
	"github.com/pingcap/dbaas/cost-metering-service/log"
	"go.uber.org/zap"
	golog "log"
	"os"
	"time"

	mysqlconfig "github.com/go-sql-driver/mysql"
	"github.com/pingcap/errors"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// globalDB represents the global database connection pool instance and all
// database operation should use db.Transaction interface to retrieve a
// transaction object.
var globalDB, globalReadOnlyDB, globalSetupDB *gorm.DB

type AuthConfig struct {
	Host     string
	Port     int
	User     string
	Password string
	Database string
}

func getDBAuthConfig(isSetupDB bool) *AuthConfig {
	ret := &AuthConfig{
		User:     config.Get().GetString(config.MySQLUser),
		Password: config.Get().GetString(config.MySQLPassword),
		Host:     config.Get().GetString(config.MySQLHost),
		Port:     config.Get().GetInt(config.MYSQLPort),
		//User:     "cost",
		//Password: "",
		//Host:     "nightly-cbp-dev-staging.cluster-c6owagykop0g.us-west-2.rds.amazonaws.com",
		//Port:     3306,
	}
	if !isSetupDB {
		ret.Database = config.Get().GetString(config.Database)
		//ret.Database = "cost_dev"

	}
	log.Info("getDBAuthConfig", zap.Any("ret", ret))
	return ret
}

func getDSN(authConfig *AuthConfig) string {
	conf := mysqlconfig.NewConfig()
	conf.Net = "tcp"
	conf.User = authConfig.User
	conf.Addr = fmt.Sprintf("%s:%d", authConfig.Host, authConfig.Port)
	conf.Passwd = authConfig.Password
	conf.DBName = authConfig.Database
	conf.ParseTime = true
	conf.Timeout = 60 * time.Second
	conf.ReadTimeout = 60 * time.Second
	conf.WriteTimeout = 60 * time.Second
	conf.Params = map[string]string{"charset": "utf8mb4"}
	conf.Collation = "utf8mb4_bin"
	return conf.FormatDSN()
}

var newLogger = logger.New(
	golog.New(os.Stdout, "\r\n", golog.LstdFlags),
	logger.Config{
		SlowThreshold:             200 * time.Millisecond,
		LogLevel:                  logger.Warn,
		IgnoreRecordNotFoundError: true,
		Colorful:                  false,
	},
)

func initializeDB(conf *AuthConfig) (*gorm.DB, error) {
	dsn := getDSN(conf)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true,
		Logger:                                   newLogger,
		TranslateError:                           true,
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	sqlDB, err := db.DB()
	if err != nil {
		return nil, errors.Trace(err)
	}
	sqlDB.SetMaxOpenConns(50)
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetConnMaxLifetime(600 * time.Second)
	return db, nil
}

func GetSetupDB() (*gorm.DB, error) {
	if globalSetupDB != nil {
		return globalSetupDB, nil
	}
	conf := getDBAuthConfig(true)
	db, err := initializeDB(conf)
	if err != nil {
		return nil, err
	}
	globalSetupDB = db
	return db, nil
}

// Initialize initializes the global database connection pool and return error
// if some unexpected errors from the driver and gorm package raised.
func Initialize() (*gorm.DB, error) {
	var err error
	conf := getDBAuthConfig(false)
	db, err := initializeDB(conf)
	if err != nil {
		return nil, err
	}
	globalDB = db
	log.Info("Initialize database successfully")
	return db, nil
}

func GetDB() *gorm.DB {
	return globalDB
}
