package tikvrel

import (
	"io"
	"math/rand"
	"strings"
	"time"

	_ "github.com/go-sql-driver/mysql"
	. "github.com/pingcap/endless/pkg/matcher"
	"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"
	_ "github.com/pingcap/test-infra/sdk/resource/impl/k8s"
)

func getRTO(ctx core.TestContext, tc resource.TiDBCluster, faultType string, faultDuration time.Duration, RTO time.Duration) time.Duration {
	// tikv may be not trigger an election if "network-delay" or "IODelay" occur
	// know issue INCALL-113 (https://github.com/tikv/client-go/issues/521), avoid this check due to issue do not fixed
	tidbReplicaRead := Try(oltppkg.GetTidbVariable(ctx, tc, "tidb_replica_read")).(string)
	if strings.Contains(faultType, "network-delay") || strings.Contains(faultType, "IODelay") || tidbReplicaRead == "follower" {
		RTO = faultDuration + 2*time.Minute
	}
	return RTO
}

func OLTP_TiKV_REL_TiKV_RESTART(
	ctx core.TestContext,
	tc resource.TiDBCluster,
	pr *io.PipeReader,
	caseCmdParms oltppkg.CaseCmdParms,
) {
	tikvRelOptions := &TiKVRelOptions{
		WarmUpTime:   10 * time.Minute,
		FaultType:    "restart",
		FaultNum:     1,
		RTO:          2 * time.Minute,
		RecoveryTime: 20 * time.Minute,
	}
	tikvRelOptions.TiKVFaultTest(ctx, tc, pr, caseCmdParms)
}

func OLTP_TiKV_REL_TiKV_DOWN(
	ctx core.TestContext,
	tc resource.TiDBCluster,
	pr *io.PipeReader,
	caseCmdParms oltppkg.CaseCmdParms,
) {
	faultType := "failure"
	faultDuration := oltppkg.GetRandDuration(time.Minute, 10) + 10*time.Minute
	RTO := getRTO(ctx, tc, faultType, faultDuration, 2*time.Minute)
	tikvRelOptions := &TiKVRelOptions{
		WarmUpTime:    10 * time.Minute,
		FaultType:     faultType,
		FaultNum:      1,
		FaultDuration: faultDuration,
		RTO:           RTO,
		RecoveryTime:  20 * time.Minute,
	}
	tikvRelOptions.TiKVFaultTest(ctx, tc, pr, caseCmdParms)
}

func OLTP_TiKV_REL_TiKV_NETWORK_LOSS(
	ctx core.TestContext,
	tc resource.TiDBCluster,
	pr *io.PipeReader,
	caseCmdParms oltppkg.CaseCmdParms,
) {
	tikvVer := oltppkg.GetCompVersion(ctx, tc, "tikv")
	curVerShort := strings.TrimSuffix(strings.TrimPrefix(tikvVer, "v"), "-pre")
	if oltppkg.CompareVersion(curVerShort, "6.0.0") < 0 {
		// known issue: https://github.com/tikv/tikv/issues/11774
		log.Info("version is less than 6.0.0, skip this case due to known issue")
		return
	}
	faultType := "network-loss-loss:100"
	faultDuration := oltppkg.GetRandDuration(time.Minute, 10) + 45*time.Minute
	RTO := getRTO(ctx, tc, faultType, faultDuration, 2*time.Minute)
	tikvRelOptions := &TiKVRelOptions{
		WarmUpTime:    10 * time.Minute,
		FaultType:     faultType,
		FaultNum:      1,
		FaultDuration: faultDuration,
		RTO:           RTO,
		RecoveryTime:  60 * time.Minute,
	}
	tikvRelOptions.TiKVFaultTest(ctx, tc, pr, caseCmdParms)
}

func OLTP_TiKV_REL_TiKV_NETWORK_DELAY(
	ctx core.TestContext,
	tc resource.TiDBCluster,
	pr *io.PipeReader,
	caseCmdParms oltppkg.CaseCmdParms,
) {
	// tikv may be not trigger an election if "network-delay" or "IODelay" occur
	faultDuration := oltppkg.GetRandDuration(time.Minute, 10) + 10*time.Minute
	tikvRelOptions := &TiKVRelOptions{
		WarmUpTime:    10 * time.Minute,
		FaultType:     "network-delay-latency:random",
		FaultNum:      1,
		FaultDuration: faultDuration,
		RTO:           faultDuration + 2*time.Minute,
		RecoveryTime:  20 * time.Minute,
	}
	tikvRelOptions.TiKVFaultTest(ctx, tc, pr, caseCmdParms)
}

func OLTP_TiKV_REL_TiKV_IO_DELAY(
	ctx core.TestContext,
	tc resource.TiDBCluster,
	pr *io.PipeReader,
	caseCmdParms oltppkg.CaseCmdParms,
) {
	// tikv may be not trigger an election if "network-delay" or "IODelay" occur
	faultDuration := oltppkg.GetRandDuration(time.Minute, 10) + 10*time.Minute
	tikvRelOptions := &TiKVRelOptions{
		WarmUpTime:    10 * time.Minute,
		FaultType:     "IODelay-delay:random",
		FaultNum:      1,
		FaultDuration: faultDuration,
		RTO:           faultDuration + 2*time.Minute,
		RecoveryTime:  20 * time.Minute,
	}
	tikvRelOptions.TiKVFaultTest(ctx, tc, pr, caseCmdParms)
}

func OLTP_TiKV_REL_TWO_TIKV_NETWORK_LOSS(
	ctx core.TestContext,
	tc resource.TiDBCluster,
	pr *io.PipeReader,
	caseCmdParms oltppkg.CaseCmdParms,
) {
	tikvVer := oltppkg.GetCompVersion(ctx, tc, "tikv")
	curVerShort := strings.TrimSuffix(strings.TrimPrefix(tikvVer, "v"), "-pre")
	if oltppkg.CompareVersion(curVerShort, "6.0.0") < 0 {
		// known issue: https://github.com/tikv/tikv/issues/11774
		log.Info("version is less than 6.0.0, skip this case due to known issue")
		return
	}
	faultType := "network-loss-loss:100"
	faultDuration := oltppkg.GetRandDuration(time.Minute, 10) + 45*time.Minute
	RTO := getRTO(ctx, tc, faultType, faultDuration, 2*time.Minute)
	tikvRelOptions := &TiKVRelOptions{
		WarmUpTime:    30 * time.Minute,
		FaultType:     faultType,
		FaultNum:      2,
		FaultDuration: faultDuration,
		RTO:           RTO,
		RecoveryTime:  60 * time.Minute,
	}
	tikvRelOptions.TiKVFaultTest(ctx, tc, pr, caseCmdParms)
}

func OLTP_TiKV_REL_TWO_TIKV_RANDOM_FAILURE(
	ctx core.TestContext,
	tc resource.TiDBCluster,
	pr *io.PipeReader,
	caseCmdParms oltppkg.CaseCmdParms,
) {
	tikvFaultTypes := []string{"failure", "network-loss-loss:100"}
	rand.Seed(time.Now().UnixNano())
	faultType := tikvFaultTypes[rand.Intn(len(tikvFaultTypes))]
	tikvRelOptions := &TiKVRelOptions{
		WarmUpTime:    30 * time.Minute,
		FaultType:     faultType,
		FaultNum:      2,
		FaultDuration: oltppkg.GetRandDuration(time.Minute, 10),
		RecoveryTime:  60 * time.Minute,
	}
	tikvRelOptions.TiKVFaultTest(ctx, tc, pr, caseCmdParms)
}

func OLTP_TiKV_REL_TWO_TIKV_NETWORK_DELAY(
	ctx core.TestContext,
	tc resource.TiDBCluster,
	pr *io.PipeReader,
	caseCmdParms oltppkg.CaseCmdParms,
) {
	faultDuration := oltppkg.GetRandDuration(time.Minute, 20) + 40*time.Minute
	tikvRelOptions := &TiKVRelOptions{
		WarmUpTime:    30 * time.Minute,
		FaultType:     "network-delay-latency:random",
		FaultNum:      2,
		FaultDuration: oltppkg.GetRandDuration(time.Minute, 20) + 40*time.Minute,
		RTO:           faultDuration + 2*time.Minute,
		RecoveryTime:  60 * time.Minute,
	}
	tikvRelOptions.TiKVFaultTest(ctx, tc, pr, caseCmdParms)
}
