package deploy

import (
	"context"
	"fmt"
	"os"
	"strconv"
	"sync/atomic"
	"testing"

	"github.com/testcontainers/testcontainers-go/modules/mysql"
	"github.com/tidbcloud/cluster-service-ng/config"
	mysql2 "github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql"
	"github.com/tidbcloud/pkgv2/gormutil"
	"gorm.io/gorm"
)

var dbConn atomic.Pointer[gorm.DB]

// Tests in this package will test service functions that interact with the schema.
// Third-party service dependencies are mocked and not tested.
func TestMain(m *testing.M) {
	if os.Getenv("NO_DOCKER") == "true" {
		return
	}
	var (
		ctx = context.Background()
		c   = &config.Config{
			ServerConfig: &config.ServerConfig{
				MysqlConfig: gormutil.Config{
					User:     "root",
					Database: "local_test", //gitleaks:allow
					Password: "password",   //gitleaks:allow
					LogLevel: "warn",
				},
			},
		}
	)

	mysqlContainer, host, port, err := startMysqlContainer(ctx, c.ServerConfig.MysqlConfig.Database, c.ServerConfig.MysqlConfig.User, c.ServerConfig.MysqlConfig.Password)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := mysqlContainer.Terminate(ctx); err != nil {
			panic(err)
		}
	}()
	c.ServerConfig.MysqlConfig.Host = host
	c.ServerConfig.MysqlConfig.Port = port

	dbConn.Store(mysql2.NewDB(&c.ServerConfig.MysqlConfig))

	m.Run()
}

func startMysqlContainer(ctx context.Context, dbName, user, passwd string) (c *mysql.MySQLContainer, host string, port uint16, err error) {
	// Refer to https://golang.testcontainers.org/features/garbage_collector/#ryuk.
	if err := os.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true"); err != nil {
		_, _ = fmt.Fprintln(os.Stderr, err)
		return nil, "", 0, err
	}
	mysqlContainer, err := mysql.Run(
		ctx,
		"gcr.io/pingcap-public/third-party/mysql:8.0",
		mysql.WithDatabase(dbName),
		mysql.WithUsername(user),
		mysql.WithPassword(passwd),
	)
	if err != nil {
		return nil, "", 0, err
	}

	host, err = mysqlContainer.Host(ctx)
	if err != nil {
		return nil, "", 0, err
	}
	portS, err := mysqlContainer.MappedPort(ctx, "3306/tcp")
	if err != nil {
		return nil, "", 0, err
	}
	portParsed, err := strconv.ParseUint(portS.Port(), 10, 16)
	if err != nil {
		return nil, "", 0, err
	}

	return mysqlContainer, host, uint16(portParsed), nil
}
