package storage

import (
	"sync/atomic"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/tidbcloud/pkgv2/db"
	"github.com/tidbcloud/pkgv2/gormutil"
	"github.com/tidbcloud/serverless-service/internal/config"
	"github.com/uptrace/opentelemetry-go-extra/otelgorm"
	"gorm.io/gorm"
)

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

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

func NewGormDB() (*gorm.DB, error) {
	if initialized.Swap(true) {
		return globalDB, nil
	}

	conf := config.Global()
	connCfg := gormutil.Config{
		User:              conf.MysqlConfig.User,
		Host:              conf.MysqlConfig.Host,
		Database:          conf.MysqlConfig.Database,
		Password:          conf.MysqlConfig.Password,
		Port:              conf.MysqlConfig.Port,
		EnableDetailedLog: conf.ServerConfig.EnableDBQueryLog,
		LogLevel:          conf.ServerConfig.LogLevel,
	}

	db, err := gormutil.Connect(connCfg)
	if err != nil {
		return nil, errors.Trace(err)
	}

	if err := db.Use(otelgorm.NewPlugin()); err != nil {
		return nil, errors.Trace(err)
	}

	if err := db.Use(&MetricsPlugin{}); err != nil {
		return nil, errors.Trace(err)
	}

	log.Info("Initialize global databases successfully")

	globalDB = db
	return db, nil
}

func NewDBProvier(gormDB *gorm.DB) (db.Provider, db.TransProvider) {
	provider := db.NewProvider(gormDB)
	return provider, provider
}
