package models

import (
	"errors"
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	"github.com/shopspring/decimal"
	"github.com/yidane/formula"
	"hongze/hongze_edb_lib/services"
	"hongze/hongze_edb_lib/services/alarm_msg"
	"hongze/hongze_edb_lib/utils"
	"strings"
	"time"
)

// CalculateRule 预测指标 规则 计算
type CalculateRule struct {
	EdbInfoId                  int `description:"指标id"`
	ConfigId                   int `description:"配置id"`
	TrendsCalculateMappingList []*PredictEdbConfCalculateMapping
	EdbInfoList                []*EdbInfo
	EdbInfoIdBytes             []string
	Formula                    string
	RuleType                   int              `description:"预测规则，1：最新，2：固定值，3：同比，4：同差，5：环比，6：环差，7：N期移动均值，8：N期段线性外推值，9：动态环差"`
	EndDate                    string           `description:"截止日期"`
	EdbInfoIdArr               []EdbInfoFromTag `description:"指标信息"`
}

// RefreshCalculateByRuleBy9 刷新计算
func RefreshCalculateByRuleBy9(rule CalculateRule) (err error) {
	o := orm.NewOrm()
	to, err := o.Begin()
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			tmpErr := to.Rollback()
			if tmpErr != nil {
				go alarm_msg.SendAlarmMsg("RefreshCalculateByRuleBy9 事务回滚失败，Err："+tmpErr.Error(), 3)
			}
		} else {
			err = to.Commit()
		}
	}()
	err = CalculateByRuleBy9(to, rule)
	return
}

// CalculateByRuleBy9 动态环差规则计算入库
func CalculateByRuleBy9(to orm.TxOrmer, rule CalculateRule) (err error) {
	realSaveDataMap := make(map[string]map[int]float64)
	saveDataMap := make(map[string]map[int]float64)
	dateList := make([]string, 0)

	formulaStr := strings.ToUpper(rule.Formula)
	// 获取关联指标数据
	for edbInfoIndex, v := range rule.EdbInfoList {
		dataList, tmpErr := GetPredictEdbDataListAll(v, 1)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		dataMap := make(map[string]float64)
		for _, dv := range dataList {
			// 现有实际数据
			if val, ok := realSaveDataMap[dv.DataTime]; ok {
				if _, ok := val[v.EdbInfoId]; !ok {
					val[v.EdbInfoId] = dv.Value
				}
			} else {
				temp := make(map[int]float64)
				temp[v.EdbInfoId] = dv.Value
				realSaveDataMap[dv.DataTime] = temp
			}

			// 待处理的数据
			if val, ok := saveDataMap[dv.DataTime]; ok {
				if _, ok := val[v.EdbInfoId]; !ok {
					val[v.EdbInfoId] = dv.Value
				}
			} else {
				temp := make(map[int]float64)
				temp[v.EdbInfoId] = dv.Value
				saveDataMap[dv.DataTime] = temp
			}

			// 以第一个指标的日期作为基准日期
			if edbInfoIndex == 0 {
				dateList = append(dateList, dv.DataTime)
			}
		}
		item := new(CalculateItems)
		item.EdbInfoId = v.EdbInfoId
		item.DataMap = dataMap
	}

	//数据处理，将日期内不全的数据做补全
	handleDateSaveDataMap(dateList, realSaveDataMap, saveDataMap, rule.EdbInfoList)

	// 添加数据
	addDataList := make([]*PredictEdbRuleData, 0)

	// 计算规则
	formulaMap := services.CheckFormula(formulaStr)

	//获取指标所有数据
	dataList := make([]*PredictEdbRuleData, 0)
	sql := `SELECT * FROM predict_edb_rule_data WHERE config_id = ?`
	_, err = to.Raw(sql, rule.ConfigId).QueryRows(&dataList)
	if err != nil {
		return err
	}
	dataMap := make(map[string]*PredictEdbRuleData)
	for _, v := range dataList {
		dataMap[v.DataTime] = v
	}
	existDataMap := make(map[string]string)

	removeDateList := make([]string, 0) //需要移除的日期
	for sk, sv := range saveDataMap {
		//fmt.Println(sk, sv)
		formulaFormStr := ReplaceFormula(rule.EdbInfoList, sv, formulaMap, formulaStr, rule.EdbInfoIdBytes)
		//计算公式异常，那么就移除该指标
		if formulaFormStr == "" {
			removeDateList = append(removeDateList, sk)
			continue
		}

		utils.FileLog.Info(fmt.Sprintf("formulaFormStr:%s", formulaFormStr))
		expression := formula.NewExpression(formulaFormStr)
		calResult, err := expression.Evaluate()
		if err != nil {
			// 分母为0的报错
			if strings.Contains(err.Error(), "divide by zero") {
				removeDateList = append(removeDateList, sk)
				continue
			}
			err = errors.New("计算失败：Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
			fmt.Println(err)
			return err
		}
		calVal, err := calResult.Float64()
		if err != nil {
			err = errors.New("计算失败：获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
			fmt.Println(err)
			return err
		}

		saveValue := decimal.NewFromFloat(calVal).RoundCeil(4).String() //utils.SubFloatToString(calVal, 4)
		existPredictEdbRuleData, ok := dataMap[sk]
		if !ok {
			dataTime, _ := time.Parse(utils.FormatDate, sk)
			timestamp := dataTime.UnixNano() / 1e6

			if _, existOk := existDataMap[sk]; !existOk {
				tmpPredictEdbRuleData := &PredictEdbRuleData{
					//PredictEdbRuleDataId: 0,
					EdbInfoId:     rule.EdbInfoId,
					ConfigId:      rule.ConfigId,
					DataTime:      sk,
					Value:         saveValue,
					CreateTime:    time.Now(),
					ModifyTime:    time.Now(),
					DataTimestamp: timestamp,
				}
				addDataList = append(addDataList, tmpPredictEdbRuleData)
			}
			existDataMap[sk] = sk
		} else {
			existValDecimal, err := decimal.NewFromString(existPredictEdbRuleData.Value)
			existStr := existValDecimal.String()
			if existStr != saveValue {
				existPredictEdbRuleData.Value = saveValue
				existPredictEdbRuleData.ModifyTime = time.Now()
				_, err = to.Update(existPredictEdbRuleData, "Value", "ModifyTime")
				if err != nil {
					return err
				}
			}
		}
	}

	// 添加计算出来的值入库
	lenAddDataList := len(addDataList)
	if lenAddDataList > 0 {
		_, err = to.InsertMulti(lenAddDataList, addDataList)
		if err != nil {
			return
		}
	}

	//删除多余的值
	lenRemoveDateList := len(removeDateList)
	if lenRemoveDateList > 0 {
		//如果拼接指标变更了，那么需要删除所有的指标数据
		sql := ` DELETE FROM predict_edb_rule_data WHERE config_id = ? and data_time in (` + utils.GetOrmInReplace(lenRemoveDateList) + `) `

		_, err = to.Raw(sql, rule.ConfigId, removeDateList).Exec()
		if err != nil {
			err = fmt.Errorf("删除计算失败的预测规则计算指标数据失败,Err:" + err.Error())
			return
		}
	}
	return
}
