package dao

import (
	"fmt"
	"sort"

	"github.com/hashicorp/go-multierror"
	"github.com/jinzhu/gorm"
	"github.com/pingcap/tispector/config"
	"github.com/pingcap/tispector/service/checker"
)

type RuleDaoImpl struct {
}

var Rule RuleDaoImpl

// 暂时没用
func (RuleDaoImpl) ListAllStatus() ([]checker.TispectorRuleStatus, error) {
	var ruleStatus []checker.TispectorRuleStatus
	return ruleStatus, GetDB().Find(&ruleStatus).Error
}

// ListAllRules 从数据库中读取所有规则并返回
func (RuleDaoImpl) ListAllRules() ([]checker.TispectorRule, error) {
	var rules []checker.TispectorRule
	return rules, GetDB().Find(&rules).Error
}

// 暂时没用
func (RuleDaoImpl) UpdateRuleStatus(ruleStatus *checker.TispectorRuleStatus) error {
	err := GetDB().Where("name = ?", ruleStatus.Name).First(&checker.TispectorRuleStatus{}).Error
	if err != nil && gorm.IsRecordNotFoundError(err) {
		return GetDB().Create(ruleStatus).Error
	}
	if err != nil {
		return err
	}

	return GetDB().Model(ruleStatus).Where("name = ?", ruleStatus.Name).Update(ruleStatus).Error
}

// InitRuleTable 会从yml默认配置中读取默认规则，以此来初始化规则数据库
func (RuleDaoImpl) InitRuleTable() error {
	var errs error
	err := GetDB().Delete(&checker.TispectorRule{}).Error
	errs = multierror.Append(errs, err)
	rulemap, _ := config.GetRule("")
	// 从map里读取数据的时候它是无序的，做个排列使结果更加易用
	keys := sort.StringSlice{}
	for key := range rulemap {
		keys = append(keys, key)
	}
	sort.Sort(keys)
	for _, key := range keys {
		fmt.Println(key)
		tispectorRule := ruleConvert(rulemap[key])
		err = GetDB().Create(tispectorRule).Error
		errs = multierror.Append(errs, err)
	}
	return errs
}

func (RuleDaoImpl) UpdateRule(RuleConf config.RuleConf) error {
	rule := ruleConvert(RuleConf)
	var errs error
	ruleFromTable, err := Rule.GetRuleByItem(rule.Item)
	errs = multierror.Append(err, errs)
	// 检查是否要替换值，是否有空值，防止污染未修改的数据
	if rule.Name != ruleFromTable.Name && rule.Name == "" {
		rule.Name = ruleFromTable.Name
	}
	if rule.Summary != ruleFromTable.Summary && rule.Summary == "" {
		rule.Summary = ruleFromTable.Summary
	}
	if rule.Content != ruleFromTable.Content && rule.Content == "" {
		rule.Content = ruleFromTable.Content
	}
	if rule.Close == 99 {
		rule.Close = ruleFromTable.Close
	}
	if rule.Level == 99 {
		rule.Level = ruleFromTable.Level
	}
	if rule.Case != ruleFromTable.Case && rule.Case == "" {
		rule.Case = ruleFromTable.Case
	}
	if rule.Position != ruleFromTable.Position && rule.Position == 99 {
		rule.Position = ruleFromTable.Position
	}
	if rule.Threshold != ruleFromTable.Threshold && rule.Threshold == "" {
		rule.Threshold = ruleFromTable.Threshold
	}
	if rule.Checkfunc != ruleFromTable.Checkfunc && rule.Checkfunc == "" {
		rule.Checkfunc = ruleFromTable.Checkfunc
	}

	// Gorm包有个缺陷，在设置整型的0，或布尔型的false时它默认不识别，需要通过map的方式传递进去
	rulemap := make(map[string]int)
	if rule.Close == 0 {

		rulemap["Close"] = 0
		err := GetDB().Model(rule).Where("item = ?", rule.Item).Update(rulemap).Error
		errs = multierror.Append(err, errs)
	}
	if rule.Position == 0 {

		rulemap["position"] = 0
		err := GetDB().Model(rule).Where("item = ?", rule.Item).Update(rulemap).Error
		errs = multierror.Append(err, errs)
	}
	err = GetDB().Model(rule).Where("item = ?", rule.Item).Update(rule).Error
	errs = multierror.Append(err, errs)
	return errs
}

func (RuleDaoImpl) GetRuleByItem(Item string) (checker.TispectorRule, error) {

	var errs error
	var rule checker.TispectorRule
	err := GetDB().First(&rule, "item = ?", Item).Error
	errs = multierror.Append(err, errs)
	return rule, errs

}

// ruleConvert 由于这个gorm包需要特定格式结构体输入，所以将ruleconf倒入一个符合格式的结构体
func ruleConvert(ruleconf config.RuleConf) (tispectorRule checker.TispectorRule) {

	tispectorRule.Item = ruleconf.Item
	tispectorRule.Name = ruleconf.Name
	tispectorRule.Summary = ruleconf.Summary
	tispectorRule.Content = ruleconf.Content
	tispectorRule.Close = ruleconf.Close
	tispectorRule.Level = ruleconf.Level
	tispectorRule.Case = ruleconf.Case
	tispectorRule.Position = ruleconf.Position
	// Gorm会把大写在数据库中匹配下划线加小写，所以不能使用驼峰命名
	tispectorRule.Checkfunc = ruleconf.CheckFunc
	tispectorRule.Threshold = ruleconf.Threshold
	return tispectorRule
}
