package staleread

import (
	"context"
	"database/sql"
	"time"

	. "github.com/onsi/gomega"
	. "github.com/pingcap/endless/pkg/matcher"
	. "github.com/pingcap/endless/pkg/matcher/metrics"
	. "github.com/pingcap/endless/pkg/util"
	"github.com/pingcap/endless/testcase/oltp/oltppkg"
	"github.com/pingcap/test-infra/sdk/resource"
	"golang.org/x/sync/errgroup"
)

func OLTP_STALE_READ_REL_001(
	ctx context.Context,
	tc resource.TiDBCluster,
	caseCmdParms oltppkg.CaseCmdParms,
	workloadRunParms oltppkg.WorkloadRunParms,
) {
	runTimeParms := oltppkg.NewRunTimePara(1*time.Hour, 10*time.Minute, oltppkg.GetRandDuration(time.Minute, 10), 20*time.Minute)
	ctxAll, cancelAll := context.WithTimeout(ctx, runTimeParms.TotalRunTime)
	defer cancelAll()
	g, ctxg := errgroup.WithContext(ctxAll)

	qpsJitterCheckParms := oltppkg.QpsJitterCheckParameters{
		IsCheck:      true,
		Offset:       "2m",
		ToleratePerc: 0.8,
		TolerateDur:  120 * time.Second,
	}
	durJitterCheckParms := oltppkg.DurJitterCheckParameters{
		IsCheck: false,
	}
	restartCheckParms := oltppkg.RestartCheckParameters{
		AllComponentRestartCheck: false,
		TiKVRestartCheck:         false,
		PDRestartCheck:           true,
		TiDBRestartCheck:         true,
	}
	oltppkg.FunCommonCheck(ctxg, tc, g, runTimeParms.TotalRunTime, workloadRunParms.Cmdpr, qpsJitterCheckParms, durJitterCheckParms, restartCheckParms)

	dbName := caseCmdParms.GetRunDbName()
	tables := oltppkg.GetAllTablesNames(ctxAll, tc, dbName)
	tableName := tables[oltppkg.GetRandInt(0, len(tables))]
	rowCount := Try(oltppkg.GetTableRows(ctxAll, tc, dbName, tableName)).(int64)
	config := Config{DBName: dbName, TotalRows: int(rowCount), Concurrency: 1, RequestInterval: 10 * time.Second, MaxStaleness: 60, TableName: tableName}
	db := Try(tc.GetDB(dbName)).(*sql.DB)
	staleRead := staleRead{Config: &config, db: db, counter: 10}

	g.Go(WithGinkgoRecover(func() {
		staleRead.Start(ctxg, tc, config)
	}))
	g.Go(WithGinkgoRecover(func() {
		ConsistentlyWithMetrics(tc).ShouldNot(ResolvedTsGapMoreThanSecond(600).For(30 * time.Second).WithCtx(ctxg))
	}))
	g.Go(WithGinkgoRecover(func() {
		oltppkg.WaitFor(ctxg, "Warm up", runTimeParms.WarmUpTime)
		instanceIdList := oltppkg.GetInstanceIdList(tc, resource.TiKV)
		instanceId, _ := oltppkg.GetRandInstance(tc, resource.TiKV)
		c, err := oltppkg.InitChaos(tc, resource.TiKV, "Down one tikv", &resource.NetworkLossSpec{Loss: "100", Correlation: "100"}, &instanceIdList[instanceId])
		Expect(err).Should(BeNil())
		c.RunPeriodically(ctxg, "@every 10m", runTimeParms.FaultDuration)
	}))

	Expect(g.Wait()).Should(BeNil())
}

func OLTP_STALE_READ_REL_002(
	ctx context.Context,
	tc resource.TiDBCluster,
	caseCmdParms oltppkg.CaseCmdParms,
	workloadRunParms oltppkg.WorkloadRunParms,
) {
	runTimeParms := oltppkg.NewRunTimePara(1*time.Hour, 10*time.Minute, oltppkg.GetRandDuration(time.Minute, 10), 20*time.Minute)
	ctxAll, cancelAll := context.WithTimeout(ctx, runTimeParms.TotalRunTime)
	defer cancelAll()
	g, ctxg := errgroup.WithContext(ctxAll)

	qpsJitterCheckParms := oltppkg.QpsJitterCheckParameters{
		IsCheck:      true,
		Offset:       "2m",
		ToleratePerc: 0.8,
		TolerateDur:  120 * time.Second,
	}
	durJitterCheckParms := oltppkg.DurJitterCheckParameters{
		IsCheck: false,
	}
	restartCheckParms := oltppkg.RestartCheckParameters{
		AllComponentRestartCheck: false,
		TiKVRestartCheck:         true,
		PDRestartCheck:           false,
		TiDBRestartCheck:         true,
	}
	oltppkg.FunCommonCheck(ctxg, tc, g, runTimeParms.TotalRunTime, workloadRunParms.Cmdpr, qpsJitterCheckParms, durJitterCheckParms, restartCheckParms)

	dbName := caseCmdParms.GetRunDbName()
	tables := oltppkg.GetAllTablesNames(ctxAll, tc, dbName)
	tableName := tables[oltppkg.GetRandInt(0, len(tables))]
	rowCount := Try(oltppkg.GetTableRows(ctxAll, tc, dbName, tableName)).(int64)
	config := Config{DBName: dbName, TotalRows: int(rowCount), Concurrency: 1, RequestInterval: 10 * time.Second, MaxStaleness: 60, TableName: tableName}
	db := Try(tc.GetDB(dbName)).(*sql.DB)
	staleRead := staleRead{Config: &config, db: db, counter: 10}

	g.Go(WithGinkgoRecover(func() {
		staleRead.Start(ctxg, tc, config)
	}))

	g.Go(WithGinkgoRecover(func() {
		oltppkg.WaitFor(ctxg, "Warm up", runTimeParms.WarmUpTime)
		c, err := oltppkg.InitChaos(tc, resource.PD, "Down pd leader", &resource.NetworkLossSpec{Loss: "100", Correlation: "100"}, oltppkg.ToBoolPtr(true))
		Expect(err).Should(BeNil())
		c.RunPeriodically(ctxg, "@every 10m", runTimeParms.FaultDuration)
	}))

	Expect(g.Wait()).Should(BeNil())
}

func OLTP_STALE_READ_FUN_001(
	ctx context.Context,
	tc resource.TiDBCluster,
	caseCmdParms oltppkg.CaseCmdParms,
	workloadRunParms oltppkg.WorkloadRunParms,
) {
	runTimeParms := oltppkg.NewRunTimePara(1*time.Hour, 20*time.Minute, oltppkg.GetRandDuration(time.Minute, 10), 20*time.Minute)
	ctxAll, cancelAll := context.WithTimeout(ctx, runTimeParms.TotalRunTime)
	defer cancelAll()
	g, ctxg := errgroup.WithContext(ctxAll)

	qpsJitterCheckParms := oltppkg.QpsJitterCheckParameters{
		IsCheck:      true,
		Offset:       "2m",
		ToleratePerc: 0.8,
		TolerateDur:  120 * time.Second,
	}
	durJitterCheckParms := oltppkg.DurJitterCheckParameters{
		IsCheck: false,
	}
	restartCheckParms := oltppkg.RestartCheckParameters{
		AllComponentRestartCheck: false,
		TiKVRestartCheck:         true,
		PDRestartCheck:           false,
		TiDBRestartCheck:         true,
	}
	oltppkg.FunCommonCheck(ctxg, tc, g, runTimeParms.TotalRunTime, workloadRunParms.Cmdpr, qpsJitterCheckParms, durJitterCheckParms, restartCheckParms)

	dbName := caseCmdParms.GetRunDbName()
	tables := oltppkg.GetAllTablesNames(ctxAll, tc, dbName)
	tableName := tables[oltppkg.GetRandInt(0, len(tables))]
	rowCount := Try(oltppkg.GetTableRows(ctxAll, tc, dbName, tableName)).(int64)
	config := Config{DBName: dbName, TotalRows: int(rowCount), Concurrency: 1, RequestInterval: 10 * time.Second, MaxStaleness: 60, TableName: tableName}
	db := Try(tc.GetDB(dbName)).(*sql.DB)
	staleRead := staleRead{Config: &config, db: db, counter: 10}

	g.Go(WithGinkgoRecover(func() {
		staleRead.Start(ctxg, tc, config)
	}))

	Expect(g.Wait()).Should(BeNil())
}
