//go:generate ${GOBIN}/mockgen -destination db_mock.go -package=test github.com/tidbcloud/pkgv2/db TransProvider
package test

import (
	"crypto/tls"
	"os"
	"strconv"
	"sync"

	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/log"
	gormutil "github.com/tidbcloud/devops/pkg/infra/utils/gorm"

	"github.com/creasty/defaults"
	"github.com/go-logr/logr"
	mysqld "github.com/go-sql-driver/mysql"
	. "github.com/onsi/ginkgo/v2"
	"github.com/onsi/ginkgo/v2/types"
	. "github.com/onsi/gomega"
	"github.com/tidbcloud/pkgv2/db"
	"go.uber.org/mock/gomock"
	"gorm.io/gorm"
)

var fw = &framework{
	Logger: logr.Discard(),
}

type framework struct {
	mu sync.Mutex

	UTDB           *gorm.DB
	UTDBProvider   db.TransProvider
	MockDBProvider *MockTransProvider
	Logger         logr.Logger
	Ctrl           *gomock.Controller
}

func ReporterConfig() types.ReporterConfig {
	_, r := GinkgoConfiguration()
	r.Verbose = true

	return r
}

func Framework() *framework {
	fw.InitLogger()
	fw.InitCtrl()
	fw.InitMockDB()
	fw.InitEnv()
	return fw
}

// InitCtrl initializes the gomock controller.
//
// NOTE: MUST call this function in the Describe block.
func (f *framework) InitCtrl() {
	BeforeEach(func() {
		f.Ctrl = gomock.NewController(GinkgoT())
	})

	AfterEach(func() {
		f.Ctrl.Finish()
	})
}

func (f *framework) InitLogger() {
	f.mu.Lock()
	defer f.mu.Unlock()

	if log.Logger.IsZero() {
		log.Logger = GinkgoLogr
	}
	if f.Logger.IsZero() {
		f.Logger = log.Logger.WithName("TestFramework")
	}
}

func (f *framework) InitEnv() {
	BeforeEach(func() {
		os.Setenv("APP_ENV", "dev")
		os.Setenv("APP_PARTITION", "global")
	})
}

func (f *framework) InitMockDB() {
	BeforeEach(func() {
		f.MockDBProvider = NewMockTransProvider(f.Ctrl)
	})
}

func (f *framework) InitUTDB() {
	f.mu.Lock()
	defer f.mu.Unlock()

	if f.UTDB != nil {
		return
	}

	var err error
	user := os.Getenv("DB_USER")
	password := os.Getenv("DB_PASSWORD")
	dbName := os.Getenv("DB_NAME")
	host := os.Getenv("DB_HOST")
	port, err := strconv.Atoi(os.Getenv("DB_PORT"))
	Expect(err).To(BeNil())

	mysqld.RegisterTLSConfig("ut", &tls.Config{
		MinVersion: tls.VersionTLS12,
		ServerName: host,
	})

	conf := config.DatabaseConfig{
		User:         user,
		Password:     password,
		Database:     dbName,
		Host:         host,
		Port:         uint16(port),
		TLSConfigKey: "ut",
	}
	Expect(defaults.Set(&conf)).To(BeNil())

	f.Logger.Info("Connecting to database ...", "host", host, "port", port, "user", user, "dbName", dbName)
	handle, err := gormutil.Connect(conf, "DevOpsUTDB")
	Expect(err).To(BeNil())
	handle.DisableForeignKeyConstraintWhenMigrating = true
	f.UTDB = handle
	f.UTDBProvider = db.NewProvider(f.UTDB)
}
