package utils

import (
	"context"
	"database/sql"
	"fmt"
	"net/url"
	"strings"
	"time"

	"github.com/Masterminds/semver/v3"
	. "github.com/onsi/ginkgo"
	. "github.com/pingcap/endless/pkg/workloadnode/tiup"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
)

func Clinic_Upload_Config(cluster *TiUPCluster, output string) {
	link, err := cluster.Diag().WithUploadMaxSize(1500000).CollectAndUpload(DiagCollectAndUploadOptions{
		Collect: DiagCollectOptions{
			Output: output,
		},
		Upload: DiagUploadOptions{
			Token: "eyJrIjoiSHBlVjFTdnI1cHlDMG5PeSIsInUiOjMyLCJpZCI6MzN9"}})
	must(err)
	log.Info("clinic link", zap.String("url", link))
}

func must(err error) {
	if err != nil {
		Fail(err.Error())
	}
}

func UpdateClusterAndConfig(ctx context.Context, tc resource.TiDBCluster, targetVersion string, config string) error {
	opt := resource.TiDBClusterUpgradeOptions{
		Version: targetVersion,
		Timeout: 30 * time.Minute,
	}
	log.Info("start to upgrade cluster")
	if err := tc.Upgrade(opt); err != nil {
		return fmt.Errorf("upgrade cluster failed: %v", err)
	}

	log.Info("start to update config")
	conf := resource.TiDBClusterUpdateConfigOptions{
		Target:  resource.TiDB,
		Config:  config,
		Timeout: 30 * time.Minute,
	}
	if err := tc.UpdateConfig(conf); err != nil {
		return fmt.Errorf("update config failed: %v", err)
	}
	return nil
}

func GetDBWithUser(tc resource.Addressable, dbName, user string) (*sql.DB, error) {
	addr, err := tc.ServiceURL(resource.DBAddr)
	if err != nil {
		return nil, err
	}
	addr.User = url.User(user)

	db, err := sql.Open("mysql", resource.BuildDSN(addr, dbName, "timeout=2m"))
	if err != nil {
		return nil, err
	}
	if err := db.Ping(); err != nil {
		return nil, err
	}
	return db, nil
}

func CheckVersion(db *sql.DB, constraint string) bool {
	var v string
	if err := db.QueryRow("select version()").Scan(&v); err != nil {
		log.Error("get version failed", zap.Error(err))
	}
	if i := strings.Index(v, "TiDB-"); i > 0 {
		v = v[i+5:]
	}
	sv := *semver.MustParse(v)
	if len(sv.Prerelease()) > 0 {
		// CI doesn't set server-version according to the semver spec. For example, if
		// the last release version of release-5.3 is v5.3.4, then the nightly version
		// of the branch should be v5.3.5-nightly, however it's set to v5.3.0-nightly
		// instead. Thus we have to call `IncPatch` many times to ensure the nightly
		// version (v5.3.0-nightly) matches version constraints like `>= v5.3.2`.
		for sv.Patch() < 99 {
			sv = sv.IncPatch()
		}
	}
	c, err := semver.NewConstraint(constraint)
	if err != nil {
		log.Error("parse constraint version failed", zap.Error(err))
	}
	ok, _ := c.Validate(&sv)
	return ok
}
