package metrics

import (
	"context"
	"fmt"
	"time"

	"github.com/golang/mock/gomock"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	mock "github.com/pingcap/endless/pkg/matcher/metrics/generated"
)

var _ = Describe("MetricsCheckers", func() {
	Context("QPSDropMoreThan", func() {
		const EmptyVectorPercentage = 0.65134
		const NonemptyScalarPercentage = 0.82156
		var any interface{}

		var mockApi *mock.MockPrometheusAPI

		BeforeEach(func() {
			mockApi = mock.NewMockPrometheusAPI(GomockController)
			mockApi.EXPECT().
				Query(gomock.Any(), gomock.Eq(genQPSDropMoreThanLabel(EmptyVectorPercentage)), gomock.Any()).
				Return(PrometheusEmptyVector, []string{}, nil).AnyTimes()
			mockApi.EXPECT().
				Query(gomock.Any(), gomock.Eq(genQPSDropMoreThanLabel(NonemptyScalarPercentage)), gomock.Any()).
				Return(PrometheusNonemptyScalar, []string{}, nil).AnyTimes()
		})

		It("should passed with NonemptyScalarPercentage", func() { // nonempty means "drop more than", thus passed.
			success, err := installMock(QPSDropMoreThan(NonemptyScalarPercentage), mockApi).Match(any)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(success).Should(BeTrue())
		})
		It("should failed with EmptyVectorPercentage", func() {
			matcher := installMock(QPSDropMoreThan(EmptyVectorPercentage), mockApi)
			success, err := matcher.Match(any)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(success).Should(BeFalse())
		})

		It("should works properly with EmptyVectorPercentage and For", func() {
			matcher := installMock(QPSDropMoreThan(NonemptyScalarPercentage).For(4*time.Second), mockApi)
			success, err := matcher.Match(any)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(success).Should(BeFalse()) // immediately False

			time.Sleep(1 * time.Second)
			success, err = matcher.Match(any)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(success).Should(BeFalse()) // one second later False

			time.Sleep(2 * time.Second)
			success, err = matcher.Match(any)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(success).Should(BeFalse()) // three seconds later False

			time.Sleep(time.Duration(1.3 * float64(time.Second)))
			success, err = matcher.Match(any)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(success).Should(BeTrue()) // five seconds later True
		})

		It("query with done channel", func() {
			ctx, cancel := context.WithCancel(context.TODO())
			cancel()

			matcher := installMock(QPSDropMoreThan(NonemptyScalarPercentage).WithCtx(ctx), mockApi)
			success, err := matcher.Match(any)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(success).Should(BeFalse())
			Expect(matcher.MatchMayChangeInTheFuture(any)).Should(BeFalse())
		})
	})
})

// keep this consistent with PromQLExpr of QPSDropMoreThan!
func genQPSDropMoreThanLabel(percentage float64) string {
	return fmt.Sprintf(`sum(irate(tidb_executor_statement_total[1m])) < %.2f * sum(rate(tidb_executor_statement_total[1m] offset 2m))`, 1-percentage)
}

func installMock(checker *checker, mock *mock.MockPrometheusAPI) *checker {
	checker.testingKnobs.mockApi = mock
	return checker
}
