package configtest

import (
	"path"
	"runtime"
	"sync"
	"testing"

	"github.com/go-sql-driver/mysql"
	"github.com/mohae/deepcopy"

	"github.com/pingcap/test-infra/services/tcms/utils"
)

var (
	defaultConfig     *Config
	loadDefaultConfig sync.Once
)

type Config struct {
	*utils.Config
	Parallel   bool
	DBFixtures []string
}

func NewTestConfig(t *testing.T, opts ...Option) *Config {
	loadDefaultConfig.Do(func() {
		_, filename, _, _ := runtime.Caller(0)
		projectRoot := path.Join(path.Dir(filename), "../..")
		defaultConfig = &Config{
			Config: utils.NewConfig(
				path.Join(projectRoot, utils.TestConfig),
				path.Join(projectRoot, utils.TestSecretConfig),
			),
		}
	})

	cfg := deepcopy.Copy(defaultConfig).(*Config)
	defaultOpts := []Option{
		WithParallel(true),
		WithDBName("_test_infra_" + t.Name()),
		WithDBFixture("tests/fixture.sql"),
	}
	for _, opt := range append(defaultOpts, opts...) {
		opt(cfg)
	}
	return cfg
}

type Option = func(*Config)

// WithParallel signals that this test is to be run in parallel with (and only with)
// other parallel tests.
// Default is true.
func WithParallel(parallel bool) Option {
	return func(c *Config) {
		c.Parallel = parallel
	}
}

// WithDBName sets the test database name.
func WithDBName(dbName string) Option {
	return func(c *Config) {
		dsn, err := mysql.ParseDSN(c.DSN)
		if err != nil {
			panic(err)
		}
		dsn.DBName = dbName
		c.DSN = dsn.FormatDSN()
	}
}

func WithDBFixture(fixturePaths ...string) Option {
	return func(c *Config) {
		c.DBFixtures = append(c.DBFixtures, fixturePaths...)
	}
}
