package examples_test

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

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"go.uber.org/zap"

	"github.com/pingcap/test-infra/sdk/resource"
	_ "github.com/pingcap/test-infra/sdk/resource/impl/k8s"

	. "github.com/pingcap/endless/pkg/matcher"
	. "github.com/pingcap/endless/pkg/matcher/metrics"
	. "github.com/pingcap/endless/pkg/util"
	"github.com/pingcap/endless/pkg/workloadnode"
)

// Use testbed:testbed_minimal.yaml with resource gotpc-workload enabled
var _ = Describe("MetricsMatcher", func() {
	ctx := suiteTestCtx
	var (
		tc     resource.TiDBCluster
		goTpc  resource.WorkloadNode
		logger *zap.Logger
	)

	BeforeEach(func() {
		tc = ctx.Resource("tc").(resource.TiDBCluster)
		goTpc = ctx.Resource("gotpc-workload").(resource.WorkloadNode)
		logger = ctx.Logger()
	})

	Context("Each spec use one metrics checker", func() {
		// Give a name `metrics_1` for this test case and decorate
		// the name with `#` mark, so that we can run the test case
		// with command: ginkgo -focus="#metrics_1#".
		TestCase("should detect tikv restart event", "metrics_1", func() {
			duration := 3 * time.Minute
			ctx2, cancel := context.WithTimeout(ctx, duration)
			defer cancel()

			// The following assertion assert that no tikv will restart.
			logger.Info("Kill tikv by yourself and this test case should fail")
			logger.Info("You may kill tikv by: kubectl exec -it tc-tikv-0 -- pkill tikv-server")
			ConsistentlyWithMetrics(tc, duration, 5*time.Second).
				ShouldNot(TiKVRestart().WithCtx(ctx2))
			logger.Info("It seems you have not killed any tikv")
		})

		TestCase("should detect qps drop", "metrics_2", func() {
			duration := 10 * time.Minute
			ctx2, cancel := context.WithTimeout(ctx, duration)
			defer cancel()

			dbURL := Try(tc.ServiceURL(resource.DBAddr)).(*url.URL)
			db := Try(tc.GetDB("")).(*sql.DB)
			_, err := db.ExecContext(ctx2, "drop database if exists tpcc;")
			Expect(err).Should(BeNil())
			_, err = db.ExecContext(ctx2, "create database tpcc;")
			Expect(err).Should(BeNil())

			goTpcCmdTpl := `/go-tpc tpcc prepare --host %s -P %s --warehouses 1000 -D tpcc -T 8`
			cmdStr := fmt.Sprintf(goTpcCmdTpl, dbURL.Hostname(), dbURL.Port())
			cmd := workloadnode.CommandContext(ctx2, cmdStr)
			Expect(cmd.Start(goTpc)).Should(BeNil())
			defer cmd.Stop()

			// Let go-tpc run for about 3 minutes. It should be at lease 2 minutes
			// because we compare the current QPS with the QPS 2 minutes before.
			time.Sleep(3 * time.Minute)
			ConsistentlyWithMetrics(tc, duration, 10*time.Second).
				Should(TolerateByDuration(QPSDropMoreThan(0.5), 30*time.Second))
			logger.Info("Terminate /go-tpc by yourself and this will fail after about 30 seconds")
			logger.Info("You may kill go-tpc by: kubectl exec -it gotpc-workload-0 -- pkill go-tpc")
			logger.Info("It seems the QPS does not drop more than 50% for about 30 seconds")
		})
	})
})
