package services

import (
	"errors"
	"eta/eta_index_lib/models"
	"eta/eta_index_lib/utils"
	"fmt"
	"github.com/shopspring/decimal"
	"github.com/yidane/formula"
	"strconv"
	"strings"
	"time"
)

// GetCalculateByRuleByNineParams 获取预测规则9的计算参数
func GetCalculateByRuleByNineParams(req models.RuleConfig) (formula string, edbInfoList []*models.EdbInfo, edbInfoIdBytes []string, err error, errMsg string) {
	formula = req.Value
	formula = strings.Replace(formula, "（", "(", -1)
	formula = strings.Replace(formula, "）", ")", -1)
	formula = strings.Replace(formula, "，", ",", -1)
	formula = strings.Replace(formula, "。", ".", -1)
	formula = strings.Replace(formula, "%", "*0.01", -1)

	//检验公式
	var checkFormulaStr string
	for _, tmpEdbInfoId := range req.EdbInfoIdArr {
		checkFormulaStr += tmpEdbInfoId.FromTag + ","
		edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
	}

	formulaSlice, err := utils.CheckFormulaJson(formula)
	if err != nil {
		errMsg = "公式格式错误，请重新填写"
		err = errors.New(errMsg)
		return
	}
	for _, f := range formulaSlice {
		formulaMap, e := utils.CheckFormula(f)
		if e != nil {
			err = fmt.Errorf("公式错误，请重新填写")
			return
		}

		for _, v := range formulaMap {
			if !strings.Contains(checkFormulaStr, v) {
				errMsg = "公式错误，请重新填写"
				err = errors.New(errMsg)
				return
			}
		}
	}

	//关联的指标信息
	edbInfoList = make([]*models.EdbInfo, 0)

	for _, tmpEdbInfoId := range req.EdbInfoIdArr {
		fromEdbInfo, tmpErr := models.GetEdbInfoById(tmpEdbInfoId.EdbInfoId)
		if tmpErr != nil {
			if tmpErr.Error() == utils.ErrNoRow() {
				err = errors.New("指标 " + strconv.Itoa(tmpEdbInfoId.EdbInfoId) + " 不存在")
			} else {
				err = errors.New("获取指标失败:Err:" + tmpErr.Error())
			}
			errMsg = "数据计算失败"
			return
		}
		edbInfoList = append(edbInfoList, fromEdbInfo)
	}

	for _, v := range formulaSlice {
		formulaMap, e := utils.CheckFormula(v)
		if e != nil {
			err = fmt.Errorf("公式错误，请重新填写")
			return
		}

		//预先计算，判断公式是否正常
		ok, _ := models.CheckFormula2(edbInfoList, formulaMap, v, edbInfoIdBytes)
		if !ok {
			errMsg = "生成计算指标失败，请使用正确的计算公式"
			err = errors.New(errMsg)
			return
		}
	}

	return
}

// CalculateByRuleByNine 动态环差规则计算入库
func CalculateByRuleByNine(formulaStr string, edbInfoList []*models.EdbInfo, edbInfoIdBytes []string, emptyType, maxEmptyType int) (dataList []*models.EdbDataList, err error) {
	realSaveDataMap := make(map[string]map[int]float64)
	saveDataMap := make(map[string]map[int]float64)
	dateList := make([]string, 0) //日期

	// 最小的结束日期 , 最晚的数据开始日期
	var minLatestDate, maxStartDate time.Time
	formulaStr = strings.ToUpper(formulaStr)
	// 获取关联指标数据
	for edbInfoIndex, v := range edbInfoList {
		// todo eta_api和eta_index_lib两个函数的逻辑不一致
		sourceDataList, tmpErr := models.GetPredictEdbDataListAll(v, 1)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		dataMap := make(map[string]float64)
		//lenData := len(sourceDataList)
		for _, dv := range sourceDataList {
			// 实际数据
			if val, ok := realSaveDataMap[dv.DataTime]; ok {
				if _, ok1 := val[v.EdbInfoId]; !ok1 {
					val[v.EdbInfoId] = dv.Value
				}
			} else {
				temp := make(map[int]float64)
				temp[v.EdbInfoId] = dv.Value
				realSaveDataMap[dv.DataTime] = temp
			}

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

			// 以第一个指标的日期作为基准日期
			if edbInfoIndex == 0 {
				dateList = append(dateList, dv.DataTime)
				tmpDate, _ := time.ParseInLocation(utils.FormatDate, dv.DataTime, time.Local)
				if minLatestDate.IsZero() || tmpDate.After(minLatestDate) {
					minLatestDate = tmpDate
				}
				if maxStartDate.IsZero() || tmpDate.Before(maxStartDate) {
					maxStartDate = tmpDate
				}
			}

			/*			if lenData > 0 {
						tmpLatestDate, _ := time.ParseInLocation(utils.FormatDate, sourceDataList[lenData-1].DataTime, time.Local)
						if minLatestDate.IsZero() || minLatestDate.After(tmpLatestDate) {
							minLatestDate = tmpLatestDate
						}

						tmpStartDate, _ := time.ParseInLocation(utils.FormatDate, sourceDataList[0].DataTime, time.Local)
						if maxStartDate.IsZero() || maxStartDate.Before(tmpStartDate) {
							maxStartDate = tmpStartDate
						}
					}*/
		}
		item := new(models.CalculateItems)
		item.EdbInfoId = v.EdbInfoId
		item.DataMap = dataMap
	}

	models.HandleDateSaveDataMap(dateList, maxStartDate, minLatestDate, realSaveDataMap, saveDataMap, edbInfoList, emptyType)
	// 添加数据
	dataList = make([]*models.EdbDataList, 0)

	// 计算规则
	formulaDateSlice, formulaDateMap, err := utils.HandleFormulaJson(formulaStr, minLatestDate)
	if err != nil {
		return
	}
	existDataMap := make(map[string]string)
	// 判断是否特殊处理max和min函数
	maxDealFlag := false
	if emptyType == 4 && maxEmptyType == 2 {
		maxDealFlag = true
	}
	for k, date := range dateList {
		sv := saveDataMap[date]
		// 当空值处理类型选择了不计算时，只要有一个指标在某个日期没有值(即空值)，则计算指标在该日期没有值
		if emptyType == 1 {
			if len(sv) != len(edbInfoList) {
				continue
			}
		}
		//fmt.Println(sk, sv)
		// 根据时间范围，选择对应的公式
		formulaMap := make(map[string]string)
		formulaStr = ""
		for _, fv := range formulaDateSlice {
			if date < fv {
				if f, ok := formulaDateMap[fv]; ok {
					formulaStr = f
					formulaMap, err = utils.CheckFormula(formulaStr)
					if err != nil {
						err = fmt.Errorf("公式错误，请重新填写")
						return
					}

				}
				break
			}
		}
		if formulaStr == "" {
			continue
		}
		svMax := make(map[int]float64)
		if maxDealFlag {
			// 特殊处理max和min函数，如果原本的值为空，则选择空值参与运算
			if svMaxData, ok := realSaveDataMap[date]; ok {
				svMax = svMaxData
			}
		}

		//fmt.Println(date, sv)

		formulaFormStr := models.ReplaceFormula(edbInfoList, sv, svMax, formulaMap, formulaStr, edbInfoIdBytes, maxDealFlag)
		if formulaFormStr == `` {
			//计算公式异常，那么就移除该指标
			continue
		}

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

		saveValue, _ := decimal.NewFromFloat(calVal).RoundCeil(4).Float64() //utils.SubFloatToString(calVal, 4)
		dataTime, _ := time.Parse(utils.FormatDate, date)
		timestamp := dataTime.UnixNano() / 1e6

		if _, existOk := existDataMap[date]; !existOk {
			tmpPredictEdbRuleData := &models.EdbDataList{
				EdbDataId:     k,
				EdbInfoId:     0,
				DataTime:      date,
				DataTimestamp: timestamp,
				Value:         saveValue,
			}
			dataList = append(dataList, tmpPredictEdbRuleData)
		}
		existDataMap[date] = date
	}
	return
}
