package jiradb

import (
	"context"
	"database/sql"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"go.uber.org/atomic"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"

	"github.com/pingcap-inc/clinic/pkg/config"
)

var globalDB *gorm.DB

var initialized atomic.Bool

func Initialize(cfg *config.DBConfig) error {
	if initialized.Swap(true) {
		return errors.New("global database is already initialized")
	}

	db, err := connect(cfg)
	if err != nil {
		return err
	}
	globalDB = db

	log.Info("Initialize global database successfully")

	return nil
}

func connect(cfg *config.DBConfig) (*gorm.DB, error) {
	db, err := gorm.Open(mysql.Open(cfg.DSN()), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		return nil, err
	}

	sqlDB, err := db.DB()
	if err != nil {
		return nil, err
	}

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

	return db, nil
}

func Transaction(f func(tx *gorm.DB) error, opts ...*sql.TxOptions) (err error) {
	ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
	defer cancel()
	defer func() {
		if r := recover(); r != nil {
			err = errors.Errorf("%v", r)
		}
	}()

	err = globalDB.WithContext(ctx).Transaction(f, opts...)
	return
}
