package main

import (
	"bytes"
	"database/sql"
	"fmt"
	"os/exec"

	_ "github.com/go-sql-driver/mysql"
	"github.com/juju/errors"
	"github.com/pingcap/schrodinger/pkg/test_client"
)

// RandGenCaseConfig is for CRUD test case.
type RandGenCaseConfig struct {
	// tidbDSN describes the location of the TiDB server to connect to.
	// It is in the standard perl format for DBI DSNs.
	tidbDSN string `toml:"tidb_dsn"`
	// mysqlDSN is used to run the same queries against TiDB server.
	mysqlDSN string `toml:"mysql_dsn"`
}

// confPair indicates grammar file and gendata file pairs.
// If gendata is "default", the default data of randgen will be used.
// If gendata is "", the origin data in specified database will be used.
var confPair = map[string]string{
	"builtin_test.yy": "builtin_test.zz",
}

func NewRandGenCase(cfg *RandGenCaseConfig) (cases []*RandGenCase) {
	cases = make([]*RandGenCase, 0, len(confPair))
	for grammar, gendata := range confPair {
		newCase := &RandGenCase{
			cfg:     cfg,
			grammar: grammar,
			gendata: gendata,
		}
		cases = append(cases, newCase)
	}
	return cases
}

type RandGenCase struct {
	cfg *RandGenCaseConfig
	// grammar indicates the grammar file.
	// The grammar file is used to generate queries.
	grammar string
	// gendata indicates the data generator file.
	// the data generator file is used to generate data.
	gendata string
}

func (r *RandGenCase) String() (result string) {
	return "--grammar=" + r.grammar + " --gendata=" + r.gendata
}

func (r *RandGenCase) Execute() (err error) {
	client.Info(r.String(), " start")
	defer func() {
		client.Info(r.String(), " end")
	}()
	client.Info(r.cfg.tidbDSN, r.cfg.mysqlDSN, r.grammar, r.gendata)
	err = r.runTest()
	return errors.Trace(err)
}

func (r *RandGenCase) runTest() error {
	err := r.createNewDB()
	if err != nil {
		return errors.Trace(err)
	}
	cmd := exec.Command("./run-test.sh", r.cfg.tidbDSN, r.cfg.mysqlDSN, r.grammar, r.gendata)
	var stdOut, stdErr bytes.Buffer
	cmd.Stdout, cmd.Stderr = &stdOut, &stdErr
	err = cmd.Run()
	if err != nil {
		return errors.Trace(err)
	}
	if stdErr.Len() != 0 {
		return errors.New(stdErr.String())
	}
	return nil
}

// We re-create the database before every test case start to avoid the influence of the dirty data
func (r *RandGenCase) createNewDB() error {
	mysqlServer, err := sql.Open("mysql", fmt.Sprintf("root:%s@tcp(%s:%s)/", mysqlPwd, mysqlIP, mysqlPort))
	defer mysqlServer.Close()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = mysqlServer.Exec("drop database if exists randgen;")
	if err != nil {
		return errors.Trace(err)
	}
	_, err = mysqlServer.Exec("create database randgen;")
	if err != nil {
		return errors.Trace(err)
	}

	tidbServer, err := sql.Open("mysql", fmt.Sprintf("root:%s@tcp(%s:%s)/", tidbPwd, tidbIP, tidbPort))
	defer tidbServer.Close()
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tidbServer.Exec("drop database if exists randgen;")
	if err != nil {
		return errors.Trace(err)
	}
	_, err = tidbServer.Exec("create database randgen;")
	return errors.Trace(err)
}
