// Package route includes the test cases for testing different proxy routes.
package route

import (
	"encoding/base64"
	"encoding/json"
	"fmt"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tidbcloud/clusterproxy/test/context"
)

type executeModel struct {
	SetGc    string `json:"setGC"`
	Query    string `json:"query"`
	Password string `json:"password"`
}

type createAdminModel struct {
	CloudAdminPassword string `json:"cloudAdminPassword"`
}

var _ = Describe("proxy routes test", func() {
	var (
		ctx *context.Context
		err error
	)
	It("should create context successfully", func() {
		ctx, err = context.New()
		Expect(err).To(Succeed())
		Expect(ctx).NotTo(BeNil())
	})

	const (
		executeRoute = "/proxy/cluster/%d/v1beta1/execute/"
		password     = "password"
	)
	var encodedPassword = base64.URLEncoding.EncodeToString([]byte(password))
	execute := func(model executeModel) ([]byte, error) {
		data, err := json.Marshal(model)
		Expect(err).To(Succeed())
		return ctx.ProxyGet(
			fmt.Sprintf(executeRoute, ctx.ClusterId),
			nil,
			nil,
			data,
		)
	}

	Context("test execute query", func() {
		It("should execute the query", func() {
			body, err := execute(executeModel{
				Query:    "SHOW databases;",
				Password: encodedPassword,
			})
			Expect(err).To(Succeed())
			Expect(body).Should(BeEmpty())
		})

		It("should execute multi-line", func() {
			body, err := execute(executeModel{
				Password: encodedPassword,
				Query: `update 
				mysql.tidb 
				set 
				VARIABLE_VALUE = "distributed" 
				where VARIABLE_NAME = "tikv_gc_mode";`,
			})
			Expect(err).To(Succeed())
			Expect(body).Should(BeEmpty())
		})

		It("should return an error", func() {
			body, err := execute(executeModel{
				Password: encodedPassword,
				Query:    "SELECT databases;",
			})
			Expect(err).ToNot(Succeed())
			Expect(body).Should(BeEmpty())
		})

		It("should set gc", func() {
			body, err := execute(executeModel{
				SetGc:    "72h",
				Password: encodedPassword,
			})
			Expect(err).To(Succeed())
			Expect(body).Should(BeEmpty())
		})
	})

	Context("test create admin user", func() {
		const (
			createAdminRoute = "/proxy/cluster/%d/v1beta1/create_admin/"
			password         = "password"
		)
		var encodedPassword = base64.URLEncoding.EncodeToString([]byte(password))
		It("should create cloud admin", func() {
			// Can only create the cloud admin at initialization time when root has all grants and password is empty
			body, err := execute(executeModel{
				Password: encodedPassword,
				Query:    "ALTER USER root IDENTIFIED BY ''",
			})
			Expect(err).To(Succeed(), "clear root password")

			data, err := json.Marshal(createAdminModel{CloudAdminPassword: encodedPassword})
			Expect(err).To(Succeed())
			body, err = ctx.ProxyGet(
				fmt.Sprintf(createAdminRoute, ctx.ClusterId),
				nil,
				nil,
				data,
			)
			Expect(err).To(Succeed(), "create cloud admin user")
			Expect(body).Should(BeEmpty())

			data, err = json.Marshal(createAdminModel{CloudAdminPassword: encodedPassword})
			Expect(err).To(Succeed())
			body, err = ctx.ProxyGet(
				fmt.Sprintf(createAdminRoute, ctx.ClusterId),
				nil,
				nil,
				data,
			)
			Expect(err).To(Succeed(), "2nd create cloud admin user")
			Expect(body).Should(BeEmpty())

			/*
				stop := forwardTiDB(*ctx)
				defer func() { stop <- struct{}{} }()
				db, err := connectTiDB("cloud_admin", password)
				Expect(err).To(Succeed(), "connect as cloud admin user")
				defer db.Close()
				_, err = db.Query("use mysql;")
				Expect(err).To(Succeed())
			*/

			// set the password back to its original value
			body, err = doChangePassword(*ctx, passwordModel{
				CurrentPassword: encodedPassword,
				NewPassword:     encodedPassword,
			})
			Expect(err).To(Succeed(), "reset root password")
			Expect(body).Should(BeEmpty())
		})
	})
})
