package functionality

import (
	"time"

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

func createAlterTest(ctx *SuiteContext) {
	It("Basic CREATE and ALTER Placement Policy", func() {
		defer func() {
			MustExec(ctx.DB, "DROP TABLE IF EXISTS t1,t2,t3,t4,t5,tt,tx")
			MustExec(ctx.DB, "DROP PLACEMENT POLICY IF EXISTS p1")
			MustExec(ctx.DB, "DROP PLACEMENT POLICY IF EXISTS p2")
			MustExec(ctx.DB, "DROP PLACEMENT POLICY IF EXISTS p3")
		}()

		MustExec(ctx.DB, "DROP TABLE IF EXISTS t1,t2,t3,t4,t5,tt,tx")
		MustExec(ctx.DB, "DROP PLACEMENT POLICY IF EXISTS p1")
		MustExec(ctx.DB, "DROP PLACEMENT POLICY IF EXISTS p2")
		MustExec(ctx.DB, "DROP PLACEMENT POLICY IF EXISTS p3")

		// CREATE TABLE and PLACEMENT POLICY
		// Error 1105: invalid placement option: primary region must be included in regions
		MustExecError(ctx.DB, 1105, "CREATE PLACEMENT POLICY p1 PRIMARY_REGION=\"us-east-2\" REGIONS=\"us-west-1\";")
		MustExec(ctx.DB, "CREATE PLACEMENT POLICY p1 PRIMARY_REGION=\"us-east-1\" REGIONS=\"us-east-1,us-west-1\";")
		MustExec(ctx.DB, "CREATE PLACEMENT POLICY p2 "+
			"LEADER_CONSTRAINTS='[+region=us-east-1,+disk=ssd]' "+
			"FOLLOWER_CONSTRAINTS='[+region=us-east-2]'",
		)
		MustExec(ctx.DB, "CREATE PLACEMENT POLICY p3 followers=2")
		MustExec(ctx.DB, "ALTER PLACEMENT POLICY p3 PRIMARY_REGION='us-east-1' REGIONS='us-east-1,us-east-2,us-west-1';")
		data := ReadAll(MustQuery(ctx.DB, "SHOW PLACEMENT"))
		Expect(data[0][0]).To(Equal("POLICY p1"))
		Expect(data[0][1]).To(Equal("PRIMARY_REGION=\"us-east-1\" REGIONS=\"us-east-1,us-west-1\""))
		Expect(data[1][0]).To(Equal("POLICY p2"))
		Expect(data[1][1]).To(Equal("LEADER_CONSTRAINTS=\"[+region=us-east-1,+disk=ssd]\" FOLLOWER_CONSTRAINTS=\"[+region=us-east-2]\""))
		Expect(data[2][0]).To(Equal("POLICY p3"))
		Expect(data[2][1]).To(Equal("PRIMARY_REGION=\"us-east-1\" REGIONS=\"us-east-1,us-east-2,us-west-1\""))

		MustExec(ctx.DB, "CREATE TABLE t1 (a INT) PLACEMENT POLICY=p2")
		Try(WaitScheduled(ctx.DB, "t1", time.Minute))
		CheckCreateTable(ctx.DB, "t1", "CREATE TABLE `t1` (\n  `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p2` */")

		// Error 8239: invalid placement option: Unknown placement policy
		MustExecError(ctx.DB, 8239, "CREATE TABLE t2 (a INT) PLACEMENT POLICY=eastandwest;")
		MustExec(ctx.DB, "CREATE TABLE t2 (a INT) PLACEMENT POLICY=p1;")
		Try(WaitScheduled(ctx.DB, "t2", time.Minute))
		// Drop a policy in use
		MustExecError(ctx.DB, 8241, "DROP PLACEMENT POLICY p1;")
		CheckCreateTable(ctx.DB, "t2", "CREATE TABLE `t2` (\n  `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p1` */")
		// Repair a table
		// Error 8215: Failed to repair table: TiDB is not in REPAIR MODE
		MustExecError(ctx.DB, 8215, "ADMIN REPAIR TABLE t2 CREATE TABLE t2 (`a` int(11) DEFAULT NULL) PLACEMENT POLICY p1;")

		MustExec(ctx.DB, "CREATE TABLE t3 (a INT) PLACEMENT POLICY=p3")
		Try(WaitScheduled(ctx.DB, "t3", time.Minute))
		CheckCreateTable(ctx.DB, "t3", "CREATE TABLE `t3` (\n  `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p3` */")

		MustExec(ctx.DB, "CREATE TABLE t4 (firstname VARCHAR(25) NOT NULL,lastname VARCHAR(25) NOT NULL,username VARCHAR(16) NOT NULL,email VARCHAR(35),joined DATE NOT NULL) PARTITION BY RANGE( YEAR(joined) ) (PARTITION p0 VALUES LESS THAN (1960) PLACEMENT POLICY=p1,PARTITION p1 VALUES LESS THAN (1970),PARTITION p2 VALUES LESS THAN (1980),PARTITION p3 VALUES LESS THAN (1990),PARTITION p4 VALUES LESS THAN MAXVALUE);")
		// Error 1659: Field 'joined' is of a not allowed type for this type of partitioning
		// It is different from test design
		MustExecError(ctx.DB, 1659, "CREATE TABLE t5 (firstname VARCHAR(25) NOT NULL,lastname VARCHAR(25) NOT NULL,username VARCHAR(16) NOT NULL,email VARCHAR(35),joined DATE NOT NULL) PLACEMENT POLICY=p1 PARTITION BY HASH(joined);")
		MustExec(ctx.DB, "CREATE TABLE t5 (a int) PLACEMENT POLICY=p1 PARTITION BY HASH(a) (PARTITION p0,  PARTITION p1)")

		MustExec(ctx.DB, "CREATE TABLE `tx` (\n"+
			"  `id` int(11) DEFAULT NULL\n"+
			") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=p2 */")
		Try(WaitScheduled(ctx.DB, "tx", time.Minute))
		CheckCreateTable(ctx.DB, "tx", "CREATE TABLE `tx` (\n  `id` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p2` */")

		// ALTER PLACEMENT POLICY
		MustExecError(ctx.DB, 8241, "DROP PLACEMENT POLICY p3;")
		MustExec(ctx.DB, "ALTER TABLE t3 PLACEMENT POLICY=DEFAULT;")
		Try(WaitScheduled(ctx.DB, "t3", time.Minute))
		CheckCreateTable(ctx.DB, "t3", "CREATE TABLE `t3` (\n  `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")
		MustExec(ctx.DB, "DROP PLACEMENT POLICY IF EXISTS p2000;")
		MustExec(ctx.DB, "DROP PLACEMENT POLICY p3;")

		MustExec(ctx.DB, "ALTER TABLE t1 PLACEMENT POLICY=p1")
		Try(WaitScheduled(ctx.DB, "t1", time.Minute))
		CheckCreateTable(ctx.DB, "t1", "CREATE TABLE `t1` (\n  `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p1` */")
		MustExecError(ctx.DB, 8239, "ALTER TABLE t1 PLACEMENT POLICY=p10")

		MustExec(ctx.DB, "ALTER TABLE t2 PLACEMENT POLICY=p2")
		Try(WaitScheduled(ctx.DB, "t2", time.Minute))
		CheckCreateTable(ctx.DB, "t2", "CREATE TABLE `t2` (\n  `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p2` */")
		MustExec(ctx.DB, "ALTER TABLE t2 RENAME TO tt")
		CheckCreateTable(ctx.DB, "tt", "CREATE TABLE `tt` (\n  `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p2` */")
		data = ReadAll(MustQuery(ctx.DB, "SHOW PLACEMENT FOR TABLE tt"))
		Expect(data).To(Equal([][]string{{"TABLE geopartition.tt", "LEADER_CONSTRAINTS=\"[+region=us-east-1,+disk=ssd]\" FOLLOWER_CONSTRAINTS=\"[+region=us-east-2]\"", "SCHEDULED"}}))

		Try(WaitScheduled(ctx.DB, "tx", time.Minute))
		CheckRegions(ctx, "tx", "", func(r Regions) {
			r.CheckLeaderPlacement("+disk=ssd,+region=us-east-1").
				CheckFollowerPlacement("+region=us-east-2", 2).
				CheckNoPeersLeft()
		})
		MustExec(ctx.DB, "ALTER TABLE tx PLACEMENT POLICY p1")
		MustExec(ctx.DB, "TRUNCATE TABLE tx")
		Try(WaitScheduled(ctx.DB, "tx", time.Minute))
		CheckCreateTable(ctx.DB, "tx", "CREATE TABLE `tx` (\n  `id` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p1` */")
		CheckRegions(ctx, "tx", "", func(r Regions) {
			r.CheckLeaderPlacement("+region=us-east-1").
				CheckFollowerPlacement("+region=us-east-1", 1).
				CheckFollowerPlacement("+region=us-west-1", 1).
				CheckNoPeersLeft()
		})
	})
}
