package tiuppkg

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

	. "github.com/onsi/gomega"
	"github.com/pingcap/endless/pkg/client/tidb"
	"github.com/pingcap/endless/pkg/matcher"
	. "github.com/pingcap/endless/pkg/matcher"
	"github.com/pingcap/endless/pkg/workload/bank"
	"github.com/pingcap/endless/pkg/workloadnode"
	"github.com/pingcap/endless/pkg/workloadnode/cluster"
	"github.com/pingcap/endless/pkg/workloadnode/tiup"
	"github.com/pingcap/endless/testcase/oltp/oltppkg"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
)

const (
	// productionMirror = "https://tiup-mirrors.pingcap.com"
	// stagingMirror    = "http://staging.tiup-server.pingcap.net"
	ProductionMirror = "http://172.16.5.134:8987"
	StagingMirror    = "http://172.16.5.139:8988"
)

type BasicCaseParams struct {
	Ctx                core.TestContext
	Tc                 cluster.BaseCluster
	Br                 resource.BR
	BenchToolset       resource.WorkloadNode
	CaseCmdParms       oltppkg.CaseCmdParms
	WorkloadRunParms   oltppkg.WorkloadRunParms
	S3Parms            oltppkg.S3Parms
	SkipAfterEachCheck chan bool
	TiupResource       *TiupCaseParams
}
type TiupCaseParams struct {
	Ctx        core.TestContext
	Version    string
	TiupMirror string
	BankConfig bank.Config
	RunTime    time.Duration
	TiupNode   resource.WorkloadNode
}

func UploadClinic(tc *tiup.TiUPCluster, flags ...string) string {
	url, _ := tc.Diag().WithUploadMaxSize(1024 * 1024 * 1024).CollectAndUpload(tiup.DiagCollectAndUploadOptions{
		Collect: tiup.DiagCollectOptions{
			Flags: flags},
		Upload: tiup.DiagUploadOptions{
			Token: "eyJrIjoiSHBlVjFTdnI1cHlDMG5PeSIsInUiOjMyLCJpZCI6MzN9"}})
	log.Info("clinic url", zap.String("url", url))
	return url
}

func SetTiupMirror(
	ctx context.Context,
	controlerNode resource.WorkloadNode,
	tiupMirror string,
) {
	var cmd string
	if tiupMirror == "staging" {
		cmd = fmt.Sprintf("tiup mirror set %s", StagingMirror)
	} else {
		cmd = fmt.Sprintf("tiup mirror set %s", ProductionMirror)
	}
	workloadCmd := workloadnode.CommandContext(ctx, cmd)
	Try(workloadCmd.Run(controlerNode))
}

func TiUPRestoreWithBR(ctx context.Context, tc *tiup.TiUPCluster, storgeUrl string) {
	tidbCli := tidb.NewClient(matcher.Try(tc.GetDB("")).(*sql.DB))
	defer tidbCli.Close()
	newCollationEnabled := matcher.Try(tidbCli.IsNewCollationEnabled()).(bool)
	log.Info("cluster new collation info", zap.Bool("enabled", newCollationEnabled))
	storage := storgeUrl + "?access-key=minioadmin&secret-access-key=minioadmin&endpoint=http%3a%2f%2fminio.pingcap.net%3a9000&force-path-style=true"
	if newCollationEnabled {

	}
	// 当前 pd url 的形式是 pd-peer.endless-htap-ch-tls-tps-840706-1-148:2379
	// {host}.{namespace}:{port}
	// 在开启 tls 的时候，如果直接使用带有 namespace 的地址，则会报错
	// Failed to get config: Get "https://pd-peer.endless-htap-ch-tls-tps-840706-1-148:2379/pd/api/v1/config": x509: certificate is valid for localhost, pd-peer, not pd-peer.endless-htap-ch-tls-tps-840706-1-148
	// 有一些方法可以绕过上述错误
	// 1. tiup deploy 前，渲染 deploy.yaml 中 pd 的 host，加上 namespace 信息
	// 2. 使用 {host}:{port} 的形式。如果出现跨 namespace 访问 pd 的时候（如 case 访问资源中的 pd 链接），则访问不了
	// 简单起见，选择第二种方式
	clusterMeta := tc.ClusterInfo.ClusterMeta
	pdURL := matcher.Try(tc.ServiceURL(resource.PDAddr)).(*url.URL)
	pdHost := strings.Split(regexp.MustCompile("https?://").ReplaceAllString(pdURL.Host, ""), ".")[0]
	// todo use cluster name variable to replace "debug"
	brTlsCmd := ""
	httpPrefix := "http"
	if clusterMeta.TLSEnabled {
		brTlsCmd = fmt.Sprintf(` --ca=%s --cert=%s --key="%s"`,
			clusterMeta.TLSCACert, clusterMeta.TLSClientCert, clusterMeta.TLSClientKey)
		httpPrefix = "https"
	}
	brCmd := fmt.Sprintf(`tiup br:%s restore full -u "%s://%s:%s" -s "%s" --send-credentials-to-tikv=true --check-requirements=false`,
		clusterMeta.ClusterVersion, httpPrefix, pdHost, pdURL.Port(), storage) + brTlsCmd

	tiupNode := tc.ControlNodeHost
	Expect(tiupNode.Run(brCmd, 5*time.Hour)).Should(BeNil())
}

func InitTiupCluster(ctx core.TestContext, tiupNode resource.WorkloadNode, topoConfig string, clusterName string, version string, tiupMirror string, cleanEnv bool) tiup.TiUPCluster {
	SetTiupMirror(ctx, tiupNode, tiupMirror)
	tiupCluster := tiup.NewTiUPCluster(tiupNode, "tiup-peer", topoConfig, clusterName).WithCMDTimeout(60 * time.Minute)
	if cleanEnv {
		tiupCluster.Destroy()
		Try(tiupCluster.Deploy(version))
		Try(tiupCluster.Start())
	} else {
		_, err := tiupCluster.Display()
		if err != nil {
			Try(tiupCluster.Deploy(version))
			Try(tiupCluster.Start())
			_, err = tiupCluster.Display()
			Expect(err).Should(BeNil())
		}
	}
	return *tiupCluster
}

func SetBlockCacheInRedhat(tc *tiup.TiUPCluster) error {
	stdout, err := tc.ControlNodeHost.Exec("cat /etc/redhat-release", 2*time.Minute)
	if err != nil {
		return err
	}
	log.Info("stdout", zap.String("stdout", stdout))
	if strings.HasPrefix(stdout, "Red Hat ") {
		log.Info("Need to reset block cache capacity.")
		//oltppkg.SetAndVerifyConfigOnline(tc, "tikv", "storage.block-cache.capacity", "16GiB")
		err := tc.UpdateConfig(resource.TiDBClusterUpdateConfigOptions{
			Target: resource.TiKV,
			Config: "memory-usage-limit = \"12G\"\n[storage.block-cache]\ncapacity = \"7G\""})
		//Config: "[storage.block-cache]\ncapacity = \"7G\""})
		return err
	}
	return nil
}
