package xorm

import (
	"context"
	"database/sql"
	"flag"
	"fmt"
	"os"
	"strings"
	"testing"

	_ "github.com/go-sql-driver/mysql"
	"github.com/go-xorm/core"
	client "github.com/pingcap/schrodinger/pkg/test_client"
)

var (
	testEngine EngineInterface
	dbType     string
	dbAddr     string
	connString string

	showSQL  = flag.Bool("show_sql", true, "show generated SQLs")
	mapType  = flag.String("map_type", "snake", "indicate the name mapping")
	cache    = flag.Bool("cache", false, "if enable cache")
	cluster  = flag.Bool("cluster", false, "if this is a cluster")
	splitter = flag.String("splitter", ";", "the splitter on connstr for cluster")
	schema   = flag.String("schema", "", "specify the schema")
)

func init() {
	cli := client.New(context.Background(), client.ModeOnline)
	dbAddr = cli.TiDBService()
	fmt.Printf("schrodinger dbAddrs are: %v\n", dbAddr)
	for i := 0; i < 4; i++ {
		dbName := "xorm_test"
		if i != 0 {
			dbName = fmt.Sprintf("%s%d", dbName, i)
		}
		dbString := fmt.Sprint("root@tcp(" + dbAddr + ")/?charset=utf8&parseTime=True")
		create(dbString, dbName)
	}
	dbType = "mysql"
	connString = "root@tcp(" + dbAddr + ")/xorm_test"
}

func createEngine(dbType, connStr string) error {
	if testEngine == nil {
		var err error

		if !*cluster {
			testEngine, err = NewEngine(dbType, connStr)
		} else {
			testEngine, err = NewEngineGroup(dbType, strings.Split(connStr, *splitter))
		}
		if err != nil {
			return err
		}

		if *schema != "" {
			testEngine.SetSchema(*schema)
		}
		testEngine.ShowSQL(*showSQL)
		testEngine.SetLogLevel(core.LOG_DEBUG)
		if *cache {
			cacher := NewLRUCacher(NewMemoryStore(), 100000)
			testEngine.SetDefaultCacher(cacher)
		}

		if len(*mapType) > 0 {
			switch *mapType {
			case "snake":
				testEngine.SetMapper(core.SnakeMapper{})
			case "same":
				testEngine.SetMapper(core.SameMapper{})
			case "gonic":
				testEngine.SetMapper(core.LintGonicMapper)
			}
		}
	}

	tables, err := testEngine.DBMetas()
	if err != nil {
		return err
	}
	var tableNames = make([]interface{}, 0, len(tables))
	for _, table := range tables {
		tableNames = append(tableNames, table.Name)
	}
	if err = testEngine.DropTables(tableNames...); err != nil {
		return err
	}
	return nil
}

func prepareEngine() error {
	return createEngine(dbType, connString)
}

func create(dbString, name string) {
	db, err := sql.Open("mysql", dbString)
	if err != nil {
		panic(err)
	}
	defer db.Close()

	_, err = db.Exec("DROP DATABASE IF EXISTS " + name)

	_, err = db.Exec("CREATE DATABASE " + name)
	if err != nil {
		panic(err)
	}

	return
}

func TestMain(m *testing.M) {
	flag.Parse()

	var res int
	fmt.Println("testing", dbType, connString)

	if err := prepareEngine(); err != nil {
		fmt.Println(err)
		return
	}

	code := m.Run()
	if code > 0 {
		res = code
	}

	os.Exit(res)
}

func TestPing(t *testing.T) {
	if err := testEngine.Ping(); err != nil {
		t.Fatal(err)
	}
}
