package tidbcluster

import (
	"context"
	"database/sql"
	"fmt"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/resource"

	"github.com/pingcap/test-infra/sdk/e2etest/framework"
)

var _ = Describe("MySQLNode Test", func() {
	Context("MySQLNode.Create", func() {
		f := framework.NewDefaultFramework("testbed-mysqlnode-create")

		It("MySQLNode's testbed should equal to testbed's name", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())
			Expect(mysql.Testbed).Should(Equal(f.Testbed()))
			Expect(mysql.Type).Should(Equal(apiresource.ResourceTypeMySQLNode))
		})

		It("MySQLNode should be ready after a while", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())

			err := f.WaitForMySQLReady(mysql, framework.TestContext.ResourceWaitReadyTimeout, false)
			framework.ExpectNoError(err)
		})

		It("MySQLNode should be created with a volume mount", func() {
			mountPath := "/path-not-exist"
			mysql := f.CreateMySQLWithVolumeMount(f.CreateTestbed(), mountPath)
			err := f.WaitForMySQLReady(mysql, framework.TestContext.ResourceWaitReadyTimeout, false)
			framework.ExpectNoError(err)

			_, _, exitCode, err := mysql.Exec(resource.WorkloadNodeExecOptions{Command: fmt.Sprintf("ls %s", mountPath)})
			framework.ExpectNoError(err)
			Expect(exitCode).Should(Equal(0))
		})

		It("MySQLNode's mysql server startup-ed", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())

			err := f.WaitForMySQLReady(mysql, framework.TestContext.ResourceWaitReadyTimeout, false)
			framework.ExpectNoError(err)

			stdout, _, _, _ := mysql.Exec(resource.WorkloadNodeExecOptions{Command: "mysql -uroot -e 'show databases'"})
			Expect(stdout).Should(Equal(
				`Database
information_schema
mysql
performance_schema
sys`))
		})
	})

	Context("MySQLNode.Get", func() {
		f := framework.NewDefaultFramework("testbed-mysqlnode-get")

		It("MySQLNode's testbed should equal to testbed's name", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())
			mysql = f.GetMySQL(mysql.Handle())

			Expect(mysql.Testbed).Should(Equal(f.Testbed()))
			Expect(mysql.Type).Should(Equal(apiresource.ResourceTypeMySQLNode))
		})

		It("MySQLNode should be ready after a while", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())
			mysql = f.GetMySQL(mysql.Handle())

			err := f.WaitForMySQLReady(mysql, framework.TestContext.ResourceWaitReadyTimeout, false)
			framework.ExpectNoError(err)
		})
	})

	Context("MySQLNode.GetEndpoints", func() {
		f := framework.NewDefaultFramework("testbed-mysqlnode-getendpoints")

		It("MySQLNode.GetEndpoints should return with DSN after ready", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())
			mysql = f.GetMySQL(mysql.Handle())

			err := f.WaitForMySQLReady(mysql, framework.TestContext.ResourceWaitReadyTimeout, true)
			framework.ExpectNoError(err)

			endpoints := f.GetMySQLEndpoints(mysql.Handle())

			Expect(len(endpoints)).Should(Equal(1))
			Expect(endpoints[0].Type).Should(Equal(apiresource.EndpointTypeDsn))

			db, err := sql.Open("mysql", endpoints[0].ExternalSvcAddress)
			framework.ExpectNoError(err)
			framework.ExpectNoError(db.PingContext(context.TODO()))
			defer db.Close()
		})
	})

	Context("MySQLNode.Delete", func() {
		f := framework.NewDefaultFramework("testbed-mysqlnode-delete")

		It("MySQLNode's delete should clean all resources", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())
			err := f.WaitForMySQLReady(mysql, framework.TestContext.ResourceWaitReadyTimeout, false)
			framework.ExpectNoError(err)

			f.DeleteMySQLWaited(mysql)

			pods := f.AllPods()
			Expect(len(pods.Items)).Should(Equal(0))
			Expect(len(f.AllPVCs().Items)).Should(Equal(0))
			Expect(len(f.AllServices().Items)).Should(Equal(0))
			Expect(len(f.AllConfigmaps().Items)).Should(Equal(1))
		})
	})

	Context("MySQLNode.Exec", func() {
		f := framework.NewDefaultFramework("testbed-mysqlnode-exec")

		It("MySQLNode's mysql --version", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())
			f.WaitForMySQLReady(mysql, framework.TestContext.ResourceWaitReadyTimeout, false)

			stdout, _, _, _ := mysql.Exec(resource.WorkloadNodeExecOptions{Command: "mysql --version"})
			Expect(stdout).Should(Equal(`mysql  Ver 8.0.25 for Linux on x86_64 (MySQL Community Server - GPL)`))
		})
	})

	Context("MySQLNode.ServiceURL", func() {
		f := framework.NewDefaultFramework("testbed-mysqlnode-service-url")

		It("MySQLNode sql.Open should work", func() {
			mysql := f.CreateMySQL(f.CreateTestbed())
			framework.ExpectNoError(f.WaitForMySQLReady(mysql, framework.TestContext.ResourceWaitReadyTimeout, true))

			dbURL, err := mysql.ServiceURL(resource.DBAddr)
			framework.ExpectNoError(err)
			db, err := sql.Open("mysql", resource.BuildDSN(dbURL, "INFORMATION_SCHEMA"))
			framework.ExpectNoError(err)
			framework.ExpectNoError(db.Ping())
		})
	})
})
