package drautosync

import (
	"context"
	"fmt"
	"net/url"
	"strings"
	"time"

	//. "github.com/onsi/gomega"
	// . "github.com/pingcap/endless/pkg/matcher"
	. "github.com/pingcap/endless/pkg/matcher"
	. "github.com/pingcap/endless/pkg/util"
	"github.com/pingcap/endless/pkg/workloadnode"
	"github.com/pingcap/endless/pkg/workloadnode/tiup"
	"github.com/pingcap/endless/testcase/oltp/oltppkg"
	"github.com/pingcap/endless/testcase/oltp/systemtest/scale"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
)

//Run recover from backup node in sync mode
func OLTP_TIUP_DR_AUTOSYNC_001(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx
	drRunTimeParams := DrRunTime{
		TotalRunTime:       2 * time.Hour,
		PrimaryRunTime:     60 * time.Minute,
		WorkloadWarmUptime: 20 * time.Minute,
		ChaosInjectTime:    5 * time.Minute,
		ChaosRecoverTime:   20 * time.Minute,
		BackupRunTime:      20 * time.Minute,
	}

	roleCurrent := caseParams.CaseCmdParms.TiupClusterConfig.TiupDrAutoSyncConfig.BackupDcRole
	log.Info("Current row", zap.String("roleCurrent", roleCurrent))
	opt := DrCaseOptions{
		DrRunTimeParms:   drRunTimeParams,
		WorkloadRunParms: caseParams.WorkloadRunParms,
		IfVerify:         false,
		IfResetTs:        false,
		Tc:               caseParams.Tc.(*tiup.TiUPCluster),
		DcNetworkDelay:   "10ms",
		SwitchToBackFlag: true,
		BackupRole:       roleCurrent,
		StopBank:         true,
	}
	log.Info(fmt.Sprintf("roleCurrent %v", opt))

	opt.DrAutoTest(ctx, caseParams)
}

//restore cluster after mode change from sync->async->sync_recover->sync
func OLTP_TIUP_DR_AUTOSYNC_002(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx

	drRunTimeParams := DrRunTime{
		TotalRunTime:       2 * time.Hour,
		PrimaryRunTime:     60 * time.Minute,
		WorkloadWarmUptime: 5 * time.Minute,
		ChaosInjectTime:    10 * time.Minute,
		ChaosRecoverTime:   30 * time.Minute,
		BackupRunTime:      20 * time.Minute,
	}
	opt := DrCaseOptions{
		DrRunTimeParms:   drRunTimeParams,
		WorkloadRunParms: caseParams.WorkloadRunParms,
		IfVerify:         false,
		IfResetTs:        false,
		Tc:               caseParams.Tc.(*tiup.TiUPCluster),
		ChaoType:         "down-backup-tikv-wait-for-sync",
		DcNetworkDelay:   "10ms",
		SwitchToBackFlag: true,
		StopBank:         true,
	}
	opt.DrAutoTest(ctx, caseParams)
}

//restore cluster after mode change from sync->async->sync_recover->sync
//by inject network partition between pd and tikv in back dc
func OLTP_TIUP_DR_AUTOSYNC_003(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx
	drRunTimeParams := DrRunTime{
		TotalRunTime:       2 * time.Hour,
		PrimaryRunTime:     60 * time.Minute,
		WorkloadWarmUptime: 10 * time.Minute,
		ChaosInjectTime:    5 * time.Minute,
		ChaosRecoverTime:   30 * time.Minute,
		BackupRunTime:      15 * time.Minute,
	}

	opt := DrCaseOptions{
		DrRunTimeParms:   drRunTimeParams,
		WorkloadRunParms: caseParams.WorkloadRunParms,
		IfVerify:         false,
		IfResetTs:        false,
		Tc:               caseParams.Tc.(*tiup.TiUPCluster),
		ChaoType:         "inject-network-partion-wait-for-sync",
		DcNetworkDelay:   "10ms",
		SwitchToBackFlag: true,
		StopBank:         true,
	}

	opt.DrAutoTest(ctx, caseParams)
}

//restore from sync-recover mode
func OLTP_TIUP_DR_AUTOSYNC_004(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx
	drRunTimeParams := DrRunTime{
		TotalRunTime:       2 * time.Hour,
		PrimaryRunTime:     60 * time.Minute,
		WorkloadWarmUptime: 10 * time.Minute,
		ChaosInjectTime:    10 * time.Minute,
		ChaosRecoverTime:   30 * time.Minute,
		BackupRunTime:      15 * time.Minute,
	}
	opt := DrCaseOptions{
		DrRunTimeParms:   drRunTimeParams,
		WorkloadRunParms: caseParams.WorkloadRunParms,
		IfVerify:         true,
		IfResetTs:        true,
		Tc:               caseParams.Tc.(*tiup.TiUPCluster),
		DcNetworkDelay:   "10ms",
		ChaoType:         "inject-backup-chaos-wait-for-sync-recover",
		SwitchToBackFlag: true,
		StopBank:         true,
	}
	opt.DrAutoTest(ctx, caseParams)
}

//restore from sync-recover mode
func OLTP_TIUP_DR_AUTOSYNC_SWITCH(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx
	drRunTimeParams := DrRunTime{
		TotalRunTime:       2 * time.Hour,
		PrimaryRunTime:     10 * time.Minute,
		WorkloadWarmUptime: 10 * time.Minute,
		BackupRunTime:      10 * time.Minute,
	}
	opt := DrCaseOptions{
		DrRunTimeParms:       drRunTimeParams,
		WorkloadRunParms:     caseParams.WorkloadRunParms,
		IfVerify:             true,
		IfResetTs:            true,
		Tc:                   caseParams.Tc.(*tiup.TiUPCluster),
		DcNetworkDelay:       "10ms",
		SwitchToBackFlag:     true,
		workloadErrCheckChan: make(chan oltppkg.WorkloadErrCheckChan, 3),
		StopBank:             true,
	}
	ctxAll, cancelAll := context.WithTimeout(ctx, opt.DrRunTimeParms.TotalRunTime)
	defer cancelAll()
	g, ctxg := errgroup.WithContext(ctxAll)
	opt.workloadErrCheckChan <- oltppkg.WorkloadErrCheckChan{IfCheck: true, IfPrint: true, IgnoreErr: strings.Split(caseParams.CaseCmdParms.TpccParms.IgnoreErrors, ",")}

	g.Go(WithGinkgoRecover(func() {
		oltppkg.WorkLoadErrLogCheckWithChan(ctxg, opt.WorkloadRunParms.Cmdpr, opt.DrRunTimeParms.TotalRunTime, opt.workloadErrCheckChan)
	}))
	oltppkg.WaitFor(ctxAll, "run workload ", opt.DrRunTimeParms.WorkloadWarmUptime)
	c := InitDrCluster(caseParams)
	tc := c.Cluster
	DrCommonCheckWithoutQPS(ctxAll, tc, g, opt.DrRunTimeParms.TotalRunTime, nil)

	c.DownBackupDc(ctxAll)
	oltppkg.WaitFor(ctxAll, "waiting for pd leader", 2*time.Minute)
	oltppkg.BasicClusterExecPdCtl(tc, "config set replication-mode majority")
	SetPlacementRule(ctxAll, caseParams.Tc, c.TiupNode, PrimaryPlacementRuleConfig)

	cmd := fmt.Sprintf("tiup cluster scale-in %s -N %s,%s,%s,%s --force -y", c.ClusterName,
		"tikv4-peer:20160", "tikv5-peer:20160", "tikv6-peer:20160", "pd3-peer:2379")
	workloadnode.CommandContext(ctxAll, cmd).Run(c.TiupNode)
	promURL := Try(tc.ServiceURL(resource.Prometheus)).(*url.URL)
	oltppkg.BasicClusterExecPdCtl(tc, "store limit all 200")
	scale.WaitForTombstone(promURL, 3, 1*time.Minute, 60*time.Minute)
	oltppkg.BasicClusterExecPdCtl(tc, "store limit all 15")

	oltppkg.BasicClusterExecPdCtl(tc, "store remove-tombstone")

	log.Info("Delete data in backup cluster")
	for _, node := range append(c.DrTikvs, c.DrPD) {
		wnode := c.NodeWorkloadMap[node]
		cmd := "rm -rf /tiup/deploy/ && rm -rf /tiup/data"
		workloadnode.CommandContext(ctxAll, cmd).Run(wnode)
	}
	log.Info("Startup backup cluster")
	c.CleanChaos()

	Try(tc.ScaleOut(DrAutoSyncTopoBackup))

	//Restart primary cluster
	c.Cluster.Display()
	oltppkg.BasicClusterExecPdCtl(tc, "config set replication-mode dr-auto-sync")
	SetDrAutoSyncPlacementRule(ctxAll, c.TiupNode, caseParams)
	WaitForReplicateStatus(tc, 30*time.Minute, 1*time.Minute, "sync")

	oltppkg.WaitFor(ctxAll, "run workload ", opt.DrRunTimeParms.BackupRunTime)

	cancelAll()
	Try(g.Wait())
}

//big cluster testting
func OLTP_TIUP_DR_AUTOSYNC_BIG_CLUSTER(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx
	drRunTimeParams := DrRunTime{
		TotalRunTime:       3 * time.Hour,
		PrimaryRunTime:     120 * time.Minute,
		WorkloadWarmUptime: 10 * time.Minute,
		ChaosInjectTime:    10 * time.Minute,
		ChaosRecoverTime:   50 * time.Minute,
		BackupRunTime:      60 * time.Minute,
	}
	opt := DrCaseOptions{
		DrRunTimeParms:   drRunTimeParams,
		WorkloadRunParms: caseParams.WorkloadRunParms,
		IfVerify:         true,
		IfResetTs:        true,
		Tc:               caseParams.Tc.(*tiup.TiUPCluster),
		DcNetworkDelay:   "10ms",
		ChaoType:         "inject-backup-chaos-wait-for-sync-recover",
		SwitchToBackFlag: true,
		StopBank:         true,
	}

	opt.DrAutoTest(ctx, caseParams)
}

func OLTP_TIUP_DR_AUTOSYNC_STABILITY_REL_001(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx
	drRunTimeParams := DrRunTime{
		TotalRunTime:       3 * time.Hour,
		PrimaryRunTime:     60 * time.Minute,
		WorkloadWarmUptime: 5 * time.Minute,
		ChaosInjectTime:    5 * time.Minute,
		ChaosRecoverTime:   30 * time.Minute,
		BackupRunTime:      5 * time.Minute,
	}

	opt := DrCaseOptions{
		DrRunTimeParms:   drRunTimeParams,
		WorkloadRunParms: caseParams.WorkloadRunParms,
		IfVerify:         true,
		IfResetTs:        true,
		Tc:               caseParams.Tc.(*tiup.TiUPCluster),
		ChaoType:         "random-chaos",
		SwitchToBackFlag: true,
	}
	opt.DrAutoTest(ctx, caseParams)
}
func OLTP_TIUP_DR_AUTOSYNC_STABILITY_REL_002(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx
	drRunTimeParams := DrRunTime{
		TotalRunTime:       3 * time.Hour,
		PrimaryRunTime:     60 * time.Minute,
		WorkloadWarmUptime: 5 * time.Minute,
		ChaosInjectTime:    5 * time.Minute,
		ChaosRecoverTime:   30 * time.Minute,
		BackupRunTime:      5 * time.Minute,
	}

	opt := DrCaseOptions{
		DrRunTimeParms:   drRunTimeParams,
		WorkloadRunParms: caseParams.WorkloadRunParms,
		IfVerify:         false,
		IfResetTs:        true,
		Tc:               caseParams.Tc.(*tiup.TiUPCluster),
		ChaoType:         "random-chaos",
		SwitchToBackFlag: true,
	}

	opt.DrAutoTest(ctx, caseParams)
}

//Run recover from backup node in sync mode
func OLTP_TIUP_DR_AUTOSYNC_PERF_001(
	caseParams *oltppkg.CaseParams,
) {
	ctx := caseParams.Ctx
	drRunTimeParams := DrRunTime{
		TotalRunTime:       4 * time.Hour,
		PrimaryRunTime:     15 * time.Minute,
		WorkloadWarmUptime: 10 * time.Minute,
	}

	for _, delay := range []string{"0ms", "1ms", "3ms", "20ms"} {
		opt := DrCaseOptions{
			DrRunTimeParms:   drRunTimeParams,
			WorkloadRunParms: caseParams.WorkloadRunParms,
			IfVerify:         false,
			IfResetTs:        false,
			Tc:               caseParams.Tc.(*tiup.TiUPCluster),
			DcNetworkDelay:   delay,
			SwitchToBackFlag: false,
		}
		opt.DrAutoTest(ctx, caseParams)
	}
}
