package util

import (
	"context"
	"errors"
	"net"
	"os"
	"sync"

	"github.com/go-sql-driver/mysql"
	"github.com/joho/godotenv"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/pkg/proxy"
	"go.uber.org/zap/zapcore"
	netproxy "golang.org/x/net/proxy"
)

// AutoConf setups almost everything related to test-infra, including
// environment variables, network proxy, logger and test context.
func AutoConf() core.TestContext {
	ctx, err := core.BuildContext()
	if err != nil {
		panic("Init test context failed.")
	}

	SetLogLevelFromEnv()

	// There are several proxy related envvar in the .env file:
	// HTTP_PROXY, MYSQL_PROXY and GRPC_PROXY.
	if err := godotenv.Load(); err != nil {
		if errors.Is(err, os.ErrNotExist) {
			log.Warn("could not find .env file.")
		} else {
			panic("Load dotenv failed.")
		}
	}

	if os.Getenv("KUBECONFIG") == "" {
		defaultKubeconfig := "./kubeconfig.yml"
		_, err := os.Stat(defaultKubeconfig)
		if err == nil {
			os.Setenv("KUBECONFIG", defaultKubeconfig)
		} else {
			log.Warn("KUBECONFIG is not set since ./kubeconfig.yml does't exist")
		}
	}

	configureMySQLDialer()

	return ctx
}

func SetLogLevelFromEnv() {
	var level zapcore.Level
	err := level.UnmarshalText([]byte(os.Getenv("ENDLESS_LOG_LEVEL")))
	if err != nil {
		panic("unable to set zap level from env ENDLESS_LOG_LEVEL")
	}
	log.SetLevel(level)
}

var (
	setMySQLProxyOnce sync.Once
)

// we need to configure MySQL dialer manually.
//
// HTTP_PROXY is set by api.DefaultRoundTripper and http.DefaultTransport
// whenever you construct a Prometheus API or fire an HTTP request.
func configureMySQLDialer() {
	dialer := proxy.MySQLProxyDialer()

	setMySQLProxyOnce.Do(func() {
		mysql.RegisterDialContext("tcp", func(ctx context.Context, addr string) (net.Conn, error) {
			if xd, ok := dialer.(netproxy.ContextDialer); ok {
				return xd.DialContext(ctx, "tcp", addr)
			}
			return dialer.Dial("tcp", addr)
		})
	})
}
