package randomddl

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

	. "github.com/onsi/ginkgo"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/resource"
	_ "github.com/pingcap/test-infra/sdk/resource/impl/k8s" // register k8s impl
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"

	"github.com/pingcap/endless/pkg/client/tidb"
	. "github.com/pingcap/endless/pkg/matcher"
	"github.com/pingcap/endless/pkg/util"
	"github.com/pingcap/endless/testcase/tiflash/pkg"
)

var _ = Describe("Random ddl test", func() {
	var (
		tc                 resource.TiDBCluster
		duration           time.Duration
		db                 *sql.DB
		tidbClient         *tidb.Client
		goroutineNum       int
		randomTimes        int
		replica            int
		tablesInfo         pkg.ExistTablesInfo
		tableAvailableTime int
	)
	Describe("Tiflash", func() {
		Context("execute random ddl", func() {
			BeforeEach(func() {
				tc = Try(suiteTestCtx.Resource("tc")).(resource.TiDBCluster)
				duration = Try(time.ParseDuration(suiteTestCtx.Param("duration", "3h"))).(time.Duration)
				db = Try(tc.GetDB("")).(*sql.DB)
				tidbClient = tidb.NewClient(db)
				goroutineNum = Try(strconv.Atoi(suiteTestCtx.Param("goroutineNum", "5"))).(int)
				replica = Try(strconv.Atoi(suiteTestCtx.Param("replicaNum", "2"))).(int)
				randomTimes = Try(strconv.Atoi(suiteTestCtx.Param("randomTimes", "2000"))).(int)
				tableAvailableTime = Try(strconv.Atoi(suiteTestCtx.Param("tableAvaiableTime", "3600"))).(int)
				tablesInfo.MaxPartition = make(map[string]int)
				Try(tidbClient.ShowClusterVersionInformation())
			})

			It("parallel execute ddl, dml, dql and scale in/out should success #001#", func() {
				// ddl + dml + dql + 扩缩容 运行 3h 不报错
				ctx, _ := context.WithTimeout(suiteTestCtx, duration)
				g, c := errgroup.WithContext(ctx)
				g.Go(util.WithGinkgoRecover(func() {
					Try(pkg.TestMultiSessionRandomDDL(c, tc, goroutineNum, replica, randomTimes, &tablesInfo, tableAvailableTime))
				}))
				g.Go(util.WithGinkgoRecover(func() {
					for {
						select {
						case <-c.Done():
							return
						default:
						}
						Try(tablesInfo.RandomDML(c, db))
					}
				}))
				g.Go(util.WithGinkgoRecover(func() {
					db2 := Try(tc.GetDB("")).(*sql.DB)
					for {
						select {
						case <-c.Done():
							return
						default:
						}
						Try(tablesInfo.RandomDQL(c, db2))
					}
				}))
				g.Go(util.WithGinkgoRecover(func() {
					componentNum := Try(tidbClient.GetAllTypeInstanceNum(suiteTestCtx)).(map[string]uint32)
					log.Info("cluster info", zap.Any("component num", componentNum))
					pkg.ScaleCluster(tc, resource.TiFlash, componentNum[resource.TiFlash.String()]+2)
					for i := 0; i <= 60; i++ {
						pkg.Sleep(ctx, time.Minute)
					}
					componentNum = Try(tidbClient.GetAllTypeInstanceNum(suiteTestCtx)).(map[string]uint32)
					log.Info("cluster info", zap.Any("component num", componentNum))
					pkg.ScaleCluster(tc, resource.TiFlash, componentNum[resource.TiFlash.String()]-2)
					for i := 0; i <= 60; i++ {
						pkg.Sleep(ctx, time.Minute)
					}
				}))
				Try(g.Wait())
			})
		})
	})
})
