// Copyright 2022 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package performance

import (
	"fmt"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/pingcap/endless/pkg/workload"
	. "github.com/pingcap/endless/testcase/geopartition/utils"
	"github.com/pingcap/log"
)

const PerfDBName = "placement_perf"

func clearPerfEnv(ctx *SuiteContext, dbName string) {
	MustExec(ctx.DB, "DROP DATABASE IF EXISTS "+dbName)
	MustExec(ctx.DB, "DROP PLACEMENT POLICY IF EXISTS systemdefault")
}

func prepareManyTables(ctx *SuiteContext, dbName string) {
	clearPerfEnv(ctx, dbName)
	MustExec(ctx.DB, "SET tidb_multi_statement_mode='ON'")
	MustExec(ctx.DB, "CREATE PLACEMENT POLICY systemdefault PRIMARY_REGION='us-east-1' REGIONS='us-east-1, us-east-2'")
	MustExec(ctx.DB, "CREATE DATABASE "+dbName+" PLACEMENT POLICY systemdefault")

	var b strings.Builder
	for i := 0; i < 1000; i++ {
		b.WriteString(fmt.Sprintf("CREATE TABLE %s.t%d (a int);", dbName, i))
		if (i+1)%50 == 0 {
			MustExec(ctx.DB, b.String())
			b.Reset()
			log.Info(fmt.Sprintf("Create table progress: %d/1000", i+1))
		}
	}

	Expect(ReadAll(MustQuery(ctx.DB, fmt.Sprintf("SHOW CREATE TABLE %s.t999", dbName)))[0][1]).To(Equal("CREATE TABLE `t999` (\n" +
		"  `a` int(11) DEFAULT NULL\n" +
		") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin " +
		"/*T![placement] PLACEMENT POLICY=`systemdefault` */"))
}

func largeMetaTest(ctx *SuiteContext) {

	It("Test many tables have placements #many_tables_with_placement#", func() {
		prepareManyTables(ctx, PerfDBName)
		defer clearPerfEnv(ctx, PerfDBName)

		MustExec(ctx.DB, "USE "+PerfDBName)
		time.Sleep(time.Second * 5)
		starTime := time.Now()
		MustExec(ctx.DB, "ALTER PLACEMENT POLICY systemdefault PRIMARY_REGION='us-east-2' REGIONS='us-east-2, us-west-1'")
		duration := time.Since(starTime)
		Expect(duration).Should(Satisfy(func(du time.Duration) bool { return du < time.Second*2 }))

		starTime = time.Now()
		scheduled := false
		for !scheduled && time.Since(starTime) <= time.Minute*5 {
			rows := ReadAll(MustQuery(ctx.DB, fmt.Sprintf("SHOW PLACEMENT WHERE TARGET LIKE 'TABLE %s.t%%' AND Scheduling_State != 'SCHEDULED'", PerfDBName)))
			if len(rows) > 0 {
				log.Info(fmt.Sprintf("Schedule not ready, left: %d", len(rows)))
				time.Sleep(time.Second * 5)
			} else {
				scheduled = true
			}
		}

		if !scheduled {
			Fail("Waiting schedule ready timeout")
		}

		CheckRegions(ctx, "t999", "", func(r Regions) {
			r.CheckLeaderPlacement("+region=us-east-2").
				CheckFollowerPlacement("+region=us-east-2", 1).
				CheckFollowerPlacement("+region=us-west-1", 1).
				CheckNoPeersLeft()
		})
	})
}
