package checker

import (
	"database/sql"
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/pingcap/enterprise-toolkit/tispector/config"
	"github.com/pingcap/enterprise-toolkit/tispector/service/db_info"
	"github.com/pingcap/enterprise-toolkit/tispector/service/task"
	"github.com/pingcap/enterprise-toolkit/tispector/util/logger"

	_ "github.com/go-sql-driver/mysql"
)

func TestHeuristic01RuleAll(t *testing.T) {
	TestRule_RuleAddCharCol(t)
	TestRule_RuleAlterViewJoin(t)
	TestRule_RuleAlterViewSubquery(t)
	TestRule_RuleAlterViewUpdate(t)
	TestRule_RuleColUseText(t)
	TestRule_RuleColUseUsignInt(t)
	TestRule_RuleDDLUseCharset(t)
	TestRule_RuleDdlIdxNoname(t)
	TestRule_RuleDmlSQLColCompute(t)
	TestRule_RuleDmlSeleNoWhere(t)
	TestRule_RuleDmlUseOrderBy(t)
	TestRule_RuleSQLCountCol(t)
	TestRule_RuleSQLMultiAutoIncr(t)
	TestRule_RuleSQLUseBlob(t)
	TestRule_RuleVarcharSmall(t)
}

func TestRule_RuleColUseText(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleCreate029")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleColUseText(audit, info)
	asst.Equal(false, pass, "TestRule_RuleColUseText failed")
}

func TestRule_RuleAddCharCol(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleAlter010")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleAddCharCol(audit, info)
	asst.Equal(false, pass, "TestRule_RuleAddCharCol failed")
}

func TestRule_RuleColUseUsignInt(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleCreate030")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleColUseUsignInt(audit, info)
	asst.Equal(false, pass, "TestRule_RuleColUseUsignInt failed")
}

func TestRule_RuleSQLMultiAutoIncr(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleCreate031")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleSQLMultiAutoIncr(audit, info)
	asst.Equal(false, pass, "TestRule_RuleSQLMultiAutoIncr failed")
}

func TestRule_RuleSQLUseBlob(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleAlter012")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleSQLUseBlob(audit, info)
	asst.Equal(false, pass, "TestRule_RuleSQLUseBlob failed")
}

func TestRule_RuleDdlIdxNoname(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleAlter013")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleDdlIdxNoname(audit, info)
	asst.Equal(false, pass, "TestRule_RuleDdlIdxNoname failed")
}

func TestRule_RuleVarcharSmall(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleAlter014")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleVarcharSmall(audit, info)
	asst.Equal(false, pass, "TestRule_RuleVarcharSmall failed")
}

func TestRule_RuleDDLUseCharset(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleAlter016")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleDDLUseCharset(audit, info)
	asst.Equal(false, pass, "TestRule_RuleDDLUseCharset failed")
}

func TestRule_RuleDmlSQLColCompute(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleDml029")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleDmlSQLColCompute(audit, info)
	asst.Equal(false, pass, "TestRule_RuleDmlSQLColCompute failed")
}

func TestRule_RuleDmlSeleNoWhere(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleDml030")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleDmlSeleNoWhere(audit, info)
	asst.Equal(false, pass, "TestRule_RuleDmlSeleNoWhere failed")
}

func TestRule_RuleDmlUseOrderBy(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleDml031")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleDmlUseOrderBy(audit, info)
	asst.Equal(false, pass, "TestRule_RuleDmlUseOrderBy failed")
}

func TestRule_RuleSQLCountCol(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleDml033")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleSQLCountCol(audit, info)
	asst.Equal(false, pass, "TestRule_RuleSQLCountCol failed")
}

func TestRule_RuleAlterViewUpdate(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleAlter021")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleAlterViewUpdate(audit, info)
	asst.Equal(false, pass, "TestRule_RuleAlterViewUpdate failed")
}

func TestRule_RuleAlterViewSubquery(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleAlter022")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleAlterViewSubquery(audit, info)
	asst.Equal(false, pass, "TestRule_RuleAlterViewSubquery failed")
}

func TestRule_RuleAlterViewJoin(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleAlter023")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleAlterViewJoin(audit, info)
	asst.Equal(false, pass, "TestRule_RuleAlterViewJoin failed")
}

///////////////////////////////////////////////////////////
//存疑规则
func TestRule_RuleSQLFormatQuery(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleDml037")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleSQLFomatQuery(audit, info)
	asst.Equal(false, pass, "TestRule_RuleSQLFormatQuery failed")
}

func TestRule_RuleCreateTablePK(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleCreate321")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleSQLFomatQuery(audit, info)
	asst.Equal(false, pass, "TestRule_RuleCreateTablePK failed")
}

func TestRule_RuleCreateTableFK(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleCreate322")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleSQLFomatQuery(audit, info)
	asst.Equal(false, pass, "TestRule_RuleCreateTableFK failed")
}

func TestRule_RuleCreateTableUseEnum(t *testing.T) {
	asst := assert.New(t)

	q := getRule("RuleCreate034")
	info := getDbInfo()
	audit := getAudit(q.Case)
	pass, _, _ := q.RuleCreateTableUseEnum(audit, info)
	asst.Equal(false, pass, "Test_RuleCreateTableUseEnum failed")
}

// TestTOOLS

func getDbInfo() *task.DBInfo {

	cluster := db_info.DbInjectionCluster{
		ID:          0,
		Name:        "dbinjection",
		Description: "1",
		Addr:        "127.0.0.1:4000",
		User:        "mysql",
		Pwd:         "mysql.1234",
		Ct:          0,
		Ut:          0,
		Operator:    "1",
	}
	var dbName string = "dbinjection"
	defaultDBName := dbName
	db, err := sql.Open("mysql",
		fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", cluster.User, cluster.Pwd, cluster.Addr, dbName))
	if err != nil {
		fmt.Printf("open db conn err: %s", err.Error())
	}

	cluster2 := db_info.DbInjectionCluster{
		ID:          0,
		Name:        "dbinjection",
		Description: "1",
		Addr:        "127.0.0.1:3306",
		User:        "mysql",
		Pwd:         "mysql.1234",
		Ct:          0,
		Ut:          0,
		Operator:    "1",
	}
	defaultDB, err := sql.Open("mysql",
		fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", cluster2.User, cluster2.Pwd, cluster2.Addr, defaultDBName))
	if err != nil {
		fmt.Printf("open db conn err: %s", err.Error())
	}
	dbInfo := task.DBInfo{DB: db, DefaultDB: defaultDB, DBName: dbName}
	return &dbInfo
}

func getAudit(sql string) *Audit {
	audit, _ := NewAudit(sql, "", "")
	return audit

}

func getRule(ruleName string) *Rule {
	RuleMap, err := config.GetRule("E:\\go\\src\\github.com\\AllinChen\\tispector\\config\\rules.yml")
	// RuleMap = &rulemap
	if err != nil {
		logger.Fatal("GetRule failed ; error: %v", err)
	}
	rule := RuleMap[ruleName]
	RuleFunc := makeFuncMap()
	var rulee Rule
	_, ok := RuleFunc[rule.CheckFunc]
	if ok {
		rulee.CheckFunc = RuleFunc[rule.CheckFunc]
	}
	if !ok {
		logger.Errorf("规则 %s 未定义执行方法", rule.Item)
		rule.Close = 0
	}

	rulee.Item = rule.Item
	rulee.Name = rule.Name
	rulee.Summary = rule.Summary
	rulee.Content = rule.Content
	if rule.Close == 1 {
		rulee.Close = false
	} else if rule.Close == 0 {
		rulee.Close = true
	} else {
		rulee.Close = true
		logger.Fatal(" The parameter  %s  setting is wrong", rule.Item)
	}
	rulee.Level = rule.Level
	rulee.Case = rule.Case
	rulee.Position = rule.Position

	rulee.Threshold = rule.Threshold
	return &rulee
}
