package global_stats

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

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	_ "github.com/pingcap/endless/pkg/client/tidb"
	. "github.com/pingcap/endless/pkg/matcher"
	"github.com/pingcap/endless/testcase/oltp/oltppkg"
	"github.com/pingcap/test-infra/sdk/resource"
)

var _ = Describe("v1static mode", func() {
	Describe("postcheck", func() {
		var (
			tc  resource.TiDBCluster
			db  *sql.DB
			ctx context.Context
		)
		BeforeEach(func() {
			tc = suiteTestCtx.Resource("tc").(resource.TiDBCluster)
			ctx, _ = context.WithTimeout(suiteTestCtx, 30*time.Minute)
			db, _ = tc.GetDB("test")
			if r := oltppkg.CompareVersionWithCurrent(ctx, tc, "5.0.0"); r > 0 {
				Try(db.Exec("set global tidb_analyze_version = 2;"))
				Try(db.Exec("set global tidb_partition_prune_mode = 'dynamic';"))
			}
			db.Close()
			// reconnet make global sysvar work
			db, _ = tc.GetDB("v1static")
		})
		When("table without config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tn")
			})
		})
		When("table with config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tcon")
			})
		})
		When("partition without config ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pn")
			})
		})
		When("partition with config ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pcon")
			})
		})
	})

	Describe("precheck", func() {
		var (
			tc  resource.TiDBCluster
			db  *sql.DB
			ctx context.Context
		)
		BeforeEach(func() {
			tc = suiteTestCtx.Resource("tc").(resource.TiDBCluster)
			ctx, _ = context.WithTimeout(suiteTestCtx, 30*time.Minute)
			db, _ = tc.GetDB("test")
			Try(db.Exec("create database if not exists v1static;"))
			if r := oltppkg.CompareVersionWithCurrent(ctx, tc, "5.0.0"); r > 0 {
				Try(db.Exec("set global tidb_analyze_version = 1;"))
				Try(db.Exec("set global tidb_partition_prune_mode = 'static';"))
			}
			db.Close()
			// reconnet make global sysvar work
			db, _ = tc.GetDB("v1static")
		})
		When("table without config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tn")
				AnalyzeTable(db, "tn", AnalyzeTableWithoutConfig)
			})
		})
		When("table with config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tcon")
				AnalyzeTable(db, "tcon", AnalyzeTableWithConfig)
			})
		})
		When("partition without config ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pn")
				AnalyzeTable(db, "pn", AnalyzePartitionWithoutConfig)
			})
		})
		When("partition with config ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pcon")
				AnalyzeTable(db, "pcon", AnalyzePartitionWithConfig)
			})
		})
	})
})

var _ = Describe("v1dynamic mode", func() {
	Describe("postcheck", func() {
		var (
			tc  resource.TiDBCluster
			db  *sql.DB
			ctx context.Context
		)
		BeforeEach(func() {
			tc = suiteTestCtx.Resource("tc").(resource.TiDBCluster)
			ctx, _ = context.WithTimeout(suiteTestCtx, 30*time.Minute)
			db, _ = tc.GetDB("test")
			if r := oltppkg.CompareVersionWithCurrent(ctx, tc, "5.0.0"); r > 0 {
				Try(db.Exec("set global tidb_analyze_version = 2;"))
				Try(db.Exec("set global tidb_partition_prune_mode = 'dynamic';"))
			}
			db.Close()
			// reconnet make global sysvar work
			db, _ = tc.GetDB("v1dynamic")
		})
		When("table without config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tn")
			})
		})
		When("table with config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tcon")
			})
		})
		When("partition without config ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pn")
			})
		})
		When("partition with config ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pcon")
			})
		})
	})

	Describe("precheck", func() {
		var (
			tc  resource.TiDBCluster
			db  *sql.DB
			ctx context.Context
		)
		BeforeEach(func() {
			tc = suiteTestCtx.Resource("tc").(resource.TiDBCluster)
			ctx, _ = context.WithTimeout(suiteTestCtx, 30*time.Minute)
			db, _ = tc.GetDB("test")
			Try(db.Exec("create database if not exists v1dynamic;"))
			if r := oltppkg.CompareVersionWithCurrent(ctx, tc, "5.0.0"); r > 0 {
				Try(db.Exec("set global tidb_analyze_version = 1;"))
				Try(db.Exec("set global tidb_partition_prune_mode = 'dynamic';"))
			}
			db.Close()
			// reconnet make global sysvar work
			db, _ = tc.GetDB("v1dynamic")
		})
		When("table without config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tn")
				AnalyzeTable(db, "tn", AnalyzeTableWithoutConfig)
			})
		})
		When("table with config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tcon")
				AnalyzeTable(db, "tcon", AnalyzeTableWithConfig)
			})
		})
		When("partition without config ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pn")
				AnalyzeTable(db, "pn", AnalyzePartitionWithoutConfig)
			})
		})
		When("partition with config ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pcon")
				AnalyzeTable(db, "pcon", AnalyzePartitionWithConfig)
			})
		})
	})
})

var _ = Describe("v2dynamic mode", func() {
	Describe("postcheck", func() {
		var (
			tc  resource.TiDBCluster
			db  *sql.DB
			ctx context.Context
		)
		BeforeEach(func() {
			tc = suiteTestCtx.Resource("tc").(resource.TiDBCluster)
			ctx, _ = context.WithTimeout(suiteTestCtx, 30*time.Minute)
			db, _ = tc.GetDB("test")
			Try(db.Exec("create database if not exists v2dynamic;"))
			if r := oltppkg.CompareVersionWithCurrent(ctx, tc, "5.0.0"); r > 0 {
				Try(db.Exec("set global tidb_partition_prune_mode = 'dynamic';"))
				Try(db.Exec("set global tidb_analyze_version = 2;"))
			}
			db.Close()
			// reconnet make global sysvar work
			db, _ = tc.GetDB("v2dynamic")
		})
		When("table without config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tn")
			})
		})
		When("table with config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tcon")
			})
		})
		When("table with columns", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tcol")
			})
		})
		When("table with columns config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tcc")
			})
		})
		When("partition without config ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pn")
			})
		})
		When("partition with config ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pcon")
			})
		})
		When("partition with columns ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pcol")
			})
		})
		When("partition with columns config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pcc")
			})
		})
	})

	Describe("precheck", func() {
		var (
			tc  resource.TiDBCluster
			db  *sql.DB
			ctx context.Context
		)
		BeforeEach(func() {
			tc = suiteTestCtx.Resource("tc").(resource.TiDBCluster)
			ctx, _ = context.WithTimeout(suiteTestCtx, 30*time.Minute)
			db, _ = tc.GetDB("test")
			Try(db.Exec("create database if not exists v2dynamic;"))
			if r := oltppkg.CompareVersionWithCurrent(ctx, tc, "5.0.0"); r > 0 {
				Try(db.Exec("set global tidb_analyze_version = 2;"))
				time.Sleep(30 * time.Second)
				Try(db.Exec("set global tidb_partition_prune_mode = 'dynamic';"))
			}
			db.Close()
			// reconnet make global sysvar work
			db, _ = tc.GetDB("v2dynamic")
		})
		When("table without config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tn")
				AnalyzeTable(db, "tn", AnalyzeTableWithoutConfig)
			})
		})
		When("table with config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tcon")
				AnalyzeTable(db, "tcon", AnalyzeTableWithConfig)
			})
		})
		// only v2 support analyze with specified columns, use config replace
		When("table with columns", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tcol")
				AnalyzeTable(db, "tcol", AnalyzeTableWithColumns)
			})
		})
		When("table with columns config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tcc")
				AnalyzeTable(db, "tcc", AnalyzeTableWithColumnsConfig)
			})
		})
		When("partition without config ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pn")
				AnalyzeTable(db, "pn", AnalyzePartitionWithoutConfig)
			})
		})
		When("partition with config ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pcon")
				AnalyzeTable(db, "pcon", AnalyzePartitionWithConfig)
			})
		})
		When("partition with columns ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pcol")
				AnalyzeTable(db, "pcol", AnalyzePartitionWithColumns)
			})
		})
		When("partition with columns config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pcc")
				AnalyzeTable(db, "pcc", AnalyzePartitionWithColumnsConfig)
			})
		})
	})
})

var _ = Describe("v2static mode", func() {
	Describe("postcheck", func() {
		var (
			tc  resource.TiDBCluster
			db  *sql.DB
			ctx context.Context
		)
		BeforeEach(func() {
			tc = suiteTestCtx.Resource("tc").(resource.TiDBCluster)
			ctx, _ = context.WithTimeout(suiteTestCtx, 30*time.Minute)
			db, _ = tc.GetDB("test")
			if r := oltppkg.CompareVersionWithCurrent(ctx, tc, "5.0.0"); r > 0 {
				Try(db.Exec("set global tidb_analyze_version = 2;"))
				Try(db.Exec("set global tidb_partition_prune_mode = 'dynamic';"))
				Try(db.Exec("create database if not exists v2static;"))
			}
			db.Close()
			// reconnet make global sysvar work
			db, _ = tc.GetDB("v2static")
		})
		When("table without config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tn")
			})
		})
		When("table with config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tcon")
			})
		})
		When("table with columns", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tcol")
			})
		})
		When("table with columns config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "tcc")
			})
		})
		When("partition without config ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pn")
			})
		})
		When("partition with config ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pcon")
			})
		})
		When("partition with columns ", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pcol")
			})
		})
		When("partition with columns config", func() {
			It("check stats status after upgrade", func() {
				PostCheck(db, "pcc")
			})
		})
	})

	Describe("precheck", func() {
		var (
			tc  resource.TiDBCluster
			db  *sql.DB
			ctx context.Context
		)
		BeforeEach(func() {
			tc = suiteTestCtx.Resource("tc").(resource.TiDBCluster)
			ctx, _ = context.WithTimeout(suiteTestCtx, 30*time.Minute)
			db, _ = tc.GetDB("test")
			Try(db.Exec("create database if not exists v2static;"))
			if r := oltppkg.CompareVersionWithCurrent(ctx, tc, "5.0.0"); r > 0 {
				Try(db.Exec("set global tidb_partition_prune_mode = 'static';"))
				Try(db.Exec("set global tidb_analyze_version = 2;"))
			}
			db.Close()
			// reconnet make global sysvar work
			db, _ = tc.GetDB("v2static")
		})
		When("table without config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tn")
				AnalyzeTable(db, "tn", AnalyzeTableWithoutConfig)
			})
		})
		When("table with config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tcon")
				AnalyzeTable(db, "tcon", AnalyzeTableWithConfig)
			})
		})
		// only v2 support analyze with specified columns, use config replace
		When("table with columns", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tcol")
				AnalyzeTable(db, "tcol", AnalyzeTableWithColumns)
			})
		})
		When("table with columns config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "tcc")
				AnalyzeTable(db, "tcc", AnalyzeTableWithColumnsConfig)
			})
		})
		When("partition without config ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pn")
				AnalyzeTable(db, "pn", AnalyzePartitionWithoutConfig)
			})
		})
		When("partition with config ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pcon")
				AnalyzeTable(db, "pcon", AnalyzePartitionWithConfig)
			})
		})
		When("partition with columns ", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pcol")
				AnalyzeTable(db, "pcol", AnalyzePartitionWithColumns)
			})
		})
		When("partition with columns config", func() {
			It("prepare data before upgrade", func() {
				PrepareData(db, "pcc")
				AnalyzeTable(db, "pcc", AnalyzePartitionWithColumnsConfig)
			})
		})
	})
})

var _ = Describe("Upgrade cluster", func() {
	It("operator upgrade", func() {
		tc := suiteTestCtx.Resource("tc").(resource.TiDBCluster)
		targetVersion := suiteTestCtx.Param("target_version", "v6.2.0-pre")
		du := Try(time.ParseDuration(suiteTestCtx.Param("update_time", "1h"))).(time.Duration)
		opt := resource.TiDBClusterUpgradeOptions{
			Version: targetVersion,
			Timeout: du,
		}
		err := tc.Upgrade(opt)
		Expect(err).Should(BeNil())
	})
})
