package logic

import (
	"errors"
	"eta/eta_index_lib/models"
	"eta/eta_index_lib/utils"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// AddPredictEdbInfo 新增预测指标
func AddPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, dataDateType string, endDateType int, ruleList []models.RuleConfig, minValue, maxValue float64, sysUserId int, sysUserName, lang string) (edbInfo *models.EdbInfo, err error, errMsg string) {
	var sourceEdbInfo *models.EdbInfo
	// 来源指标信息校验
	{
		sourceEdbInfo, err = models.GetEdbInfoById(sourceEdbInfoId)
		if err != nil && err.Error() != utils.ErrNoRow() {
			errMsg = "新增失败"
			err = errors.New("获取来源指标失败,Err:" + err.Error())
			return
		}
		if sourceEdbInfo == nil {
			errMsg = "找不到该来源指标"
			err = errors.New(errMsg)
			return
		}
		//必须是普通的指标
		if sourceEdbInfo.EdbInfoType != 0 {
			errMsg = "来源指标异常,不是普通的指标"
			err = errors.New(errMsg)
			return
		}
		//if !utils.InArrayByStr([]string{"日度", "周度", "月度", "年度"}, sourceEdbInfo.Frequency) {
		//	errMsg = "预测指标只支持选择日度、周度、月度、年度的指标"
		//	err = errors.New(errMsg)
		//	return
		//}
	}

	var classifyInfo *models.EdbClassify
	// 来源分类信息校验
	{
		classifyInfo, err = models.GetEdbClassifyById(classifyId)
		if err != nil && err.Error() != utils.ErrNoRow() {
			errMsg = "新增失败"
			err = errors.New("获取预测指标分类失败,Err:" + err.Error())
			return
		}
		if classifyInfo == nil {
			errMsg = "找不到该预测指标分类"
			err = errors.New(errMsg)
			return
		}
		//必须是预测指标分类
		if classifyInfo.ClassifyType != 1 {
			errMsg = "预测指标分类异常,不是预测指标分类"
			err = errors.New(errMsg)
			return
		}
	}

	edbName = strings.Trim(edbName, " ")

	edbCode := sourceEdbInfo.EdbCode + "_" + time.Now().Format(utils.FormatShortDateTimeUnSpace)

	// 根据指标名称和指标ID校验库中是否还存在其他同名指标
	existEdbName, err := CheckExistByEdbNameAndEdbInfoId(utils.PREDICT_EDB_INFO_TYPE, 0, edbName, lang)
	if err != nil {
		errMsg = "判断指标名称是否存在失败"
		err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
		return
	}
	if existEdbName {
		errMsg = "指标名称已存在,请重新填写"
		err = errors.New(errMsg)
		return
	}

	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
	if dataDateType == `` {
		dataDateType = `自然日`
	}

	edbInfo = &models.EdbInfo{
		//EdbInfoId:   0,
		EdbInfoType:      1,
		SourceName:       "预测指标",
		Source:           utils.DATA_SOURCE_PREDICT,
		EdbCode:          edbCode,
		EdbName:          edbName,
		EdbNameSource:    edbName,
		Frequency:        sourceEdbInfo.Frequency,
		Unit:             sourceEdbInfo.Unit,
		StartDate:        sourceEdbInfo.StartDate,
		ClassifyId:       classifyId,
		SysUserId:        sysUserId,
		SysUserRealName:  sysUserName,
		UniqueCode:       utils.MD5(utils.DATA_PREFIX + "_" + timestamp),
		CreateTime:       time.Now(),
		ModifyTime:       time.Now(),
		MinValue:         minValue,
		MaxValue:         maxValue,
		CalculateFormula: sourceEdbInfo.CalculateFormula,
		EdbType:          1,
		//Sort:             sourceEdbInfo.,
		LatestDate:    sourceEdbInfo.LatestDate,
		LatestValue:   sourceEdbInfo.LatestValue,
		MoveType:      sourceEdbInfo.MoveType,
		MoveFrequency: sourceEdbInfo.MoveFrequency,
		NoUpdate:      sourceEdbInfo.NoUpdate,
		ServerUrl:     "",
		EdbNameEn:     edbName,
		UnitEn:        sourceEdbInfo.UnitEn,
		DataDateType:  dataDateType,
		Sort:          models.GetAddEdbMaxSortByClassifyId(classifyId, utils.PREDICT_EDB_INFO_TYPE),
		EndDateType:   endDateType,
	}

	// 关联关系表
	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
	fromEdbMap := make(map[int]int)

	// 源指标关联关系表
	calculateMappingItem := &models.EdbInfoCalculateMapping{
		//EdbInfoCalculateMappingId: 0,
		//EdbInfoId:                 0,
		Source:         edbInfo.Source,
		SourceName:     edbInfo.SourceName,
		EdbCode:        edbInfo.EdbCode,
		FromEdbInfoId:  sourceEdbInfo.EdbInfoId,
		FromEdbCode:    sourceEdbInfo.EdbCode,
		FromEdbName:    sourceEdbInfo.EdbName,
		FromSource:     sourceEdbInfo.Source,
		FromSourceName: sourceEdbInfo.SourceName,
		//FromTag:        "",
		Sort:       1,
		CreateTime: time.Now(),
		ModifyTime: time.Now(),
	}
	fromEdbMap[sourceEdbInfoId] = sourceEdbInfoId
	calculateMappingList = append(calculateMappingList, calculateMappingItem)

	// 动态环差 计算列表
	calculateRuleMap := make(map[int]models.CalculateRule, 0)

	// 预测指标配置
	predictEdbConfList := make([]*models.PredictEdbConf, 0)
	var ruleEndDate time.Time
	for ruleIndex, v := range ruleList {
		if endDateType == 0 {
			// 预测指标配置
			ruleEndDate, err = time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
			if err != nil {
				errMsg = "规则配置的截止日期异常,请重新填写"
				err = errors.New(errMsg)
				return
			}
		} else {
			if v.EndNum <= 0 {
				errMsg = "截止期数不正确,请输入大于等于1的整数"
				err = errors.New(errMsg)
				return
			}
		}
		//1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值,9:动态环差,10:根据 给定终值后插值 规则获取预测数据,11:根据 季节性 规则获取预测数据,12:根据 移动平均同比 规则获取预测数据
		// 环比、环差、动态环差、季节性、移动平均同比不支持年度
		if sourceEdbInfo.Frequency == "年度" && utils.InArrayByInt([]int{5, 6, 11, 12}, v.RuleType) {
			errMsg = "环比、环差、动态环差、季节性、移动平均同比不支持年度指标"
			err = errors.New(errMsg)
			return
		}

		if v.RuleType == 16 && endDateType != 1 {
			errMsg = "年度值倒推不支持截止期数"
			err = errors.New(errMsg)
			return
		}
		switch v.RuleType {
		case 8: //N期段线性外推值
			valInt, tmpErr := strconv.Atoi(v.Value)
			if tmpErr != nil {
				errMsg = "N期段线性外推值的N值异常"
				err = errors.New(errMsg)
				return
			}
			if valInt <= 1 {
				errMsg = "N期段线性外推值的N值必须大于1"
				err = errors.New(errMsg)
				return
			}
		case 9: //9:动态环差
			if v.Value == "" {
				errMsg = "请填写计算规则"
				err = errors.New(errMsg)
				return
			}
			formula := v.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)
			v.Value = formula

			//检验公式
			var formulaStr string
			var edbInfoIdBytes []string
			for _, tmpEdbInfoId := range v.EdbInfoIdArr {
				formulaStr += tmpEdbInfoId.FromTag + ","
				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
			}
			formulaSlice, tErr := utils.CheckFormulaJson(formula)
			if tErr != nil {
				errMsg = "公式格式错误,请重新填写"
				err = errors.New(errMsg)
				return
			}
			for _, fm := range formulaSlice {
				formulaMap, e := utils.CheckFormula(fm)
				if e != nil {
					err = fmt.Errorf("公式错误,请重新填写")
					return
				}
				for _, f := range formulaMap {
					if !strings.Contains(formulaStr, f) {
						errMsg = "公式错误,请重新填写"
						err = errors.New(errMsg)
						return
					}
				}
			}

			//关联的指标信息
			edbInfoList := make([]*models.EdbInfo, 0)
			// 动态环差规则 关系表
			trendsMappingList := make([]*models.PredictEdbConfCalculateMapping, 0)

			for k, tmpEdbInfoId := range v.EdbInfoIdArr {
				fromEdbInfo, tmpErr := models.GetEdbInfoById(tmpEdbInfoId.EdbInfoId)
				if tmpErr != nil {
					err = tmpErr
					if err.Error() == utils.ErrNoRow() {
						errMsg = "指标 " + strconv.Itoa(tmpEdbInfoId.EdbInfoId) + " 不存在"
						err = errors.New(errMsg)
						return
					}
					errMsg = "获取指标失败:Err:" + err.Error()
					err = errors.New(errMsg)
					return
				}

				edbInfoList = append(edbInfoList, fromEdbInfo)

				//总的 预测指标与所有相关联指标的关系表(不仅仅该条规则)
				{
					if _, ok := fromEdbMap[tmpEdbInfoId.EdbInfoId]; !ok {
						fromEdbMap[tmpEdbInfoId.EdbInfoId] = tmpEdbInfoId.EdbInfoId
						calculateMappingItem := &models.EdbInfoCalculateMapping{
							EdbInfoCalculateMappingId: 0,
							EdbInfoId:                 0,
							Source:                    edbInfo.Source,
							SourceName:                edbInfo.SourceName,
							EdbCode:                   "",
							FromEdbInfoId:             fromEdbInfo.EdbInfoId,
							FromEdbCode:               fromEdbInfo.EdbCode,
							FromEdbName:               fromEdbInfo.EdbName,
							FromSource:                fromEdbInfo.Source,
							FromSourceName:            fromEdbInfo.SourceName,
							//FromTag:                   tmpEdbInfoId.FromTag,
							Sort:       k + 1,
							CreateTime: time.Now(),
							ModifyTime: time.Now(),
						}
						calculateMappingList = append(calculateMappingList, calculateMappingItem)
					}
				}
				// 动态环差规则 关系表
				tmpPredictEdbConfCalculateMapping := &models.PredictEdbConfCalculateMapping{
					//PredictEdbConfCalculateMappingId: 0,
					EdbInfoId:      0,
					ConfigId:       0,
					FromEdbInfoId:  fromEdbInfo.EdbInfoId,
					FromEdbCode:    fromEdbInfo.EdbCode,
					FromEdbName:    fromEdbInfo.EdbName,
					FromSource:     fromEdbInfo.Source,
					FromSourceName: fromEdbInfo.SourceName,
					FromTag:        tmpEdbInfoId.FromTag,
					Sort:           k + 1,
					CreateTime:     time.Now(),
					ModifyTime:     time.Now(),
				}
				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
			}
			for _, f := range formulaSlice {
				formulaMap, e := utils.CheckFormula(f)
				if e != nil {
					err = fmt.Errorf("公式错误,请重新填写")
					return
				}
				//预先计算,判断公式是否正常
				ok, _ := models.CheckFormula2(edbInfoList, formulaMap, f, edbInfoIdBytes)
				if !ok {
					errMsg = "生成计算指标失败,请使用正确的计算公式"
					err = errors.New(errMsg)
					return
				}
			}

			calculateRuleMap[ruleIndex] = models.CalculateRule{
				TrendsCalculateMappingList: trendsMappingList,
				EdbInfoList:                edbInfoList,
				EdbInfoIdBytes:             edbInfoIdBytes,
				Formula:                    formula,
				RuleType:                   v.RuleType,
				EndDate:                    v.EndDate,
				EdbInfoIdArr:               v.EdbInfoIdArr,
			}
		case 14: //14:根据 一元线性拟合 规则获取预测数据
			if v.Value == "" {
				errMsg = "请填写一元线性拟合规则"
				err = errors.New(errMsg)
				return
			}

			//关联的指标信息
			edbInfoList := make([]*models.EdbInfo, 0)
			// 动态环差规则 关系表
			trendsMappingList := make([]*models.PredictEdbConfCalculateMapping, 0)

			for k, tmpEdbInfoId := range v.EdbInfoIdArr {
				fromEdbInfo, tmpErr := models.GetEdbInfoById(tmpEdbInfoId.EdbInfoId)
				if tmpErr != nil {
					err = tmpErr
					if err.Error() == utils.ErrNoRow() {
						errMsg = "指标 " + strconv.Itoa(tmpEdbInfoId.EdbInfoId) + " 不存在"
						err = errors.New(errMsg)
						return
					}
					errMsg = "获取指标失败:Err:" + err.Error()
					err = errors.New(errMsg)
					return
				}

				edbInfoList = append(edbInfoList, fromEdbInfo)

				//总的 预测指标与所有相关联指标的关系表(不仅仅该条规则)
				{
					if _, ok := fromEdbMap[tmpEdbInfoId.EdbInfoId]; !ok {
						fromEdbMap[tmpEdbInfoId.EdbInfoId] = tmpEdbInfoId.EdbInfoId
						tmpCalculateMappingItem := &models.EdbInfoCalculateMapping{
							EdbInfoCalculateMappingId: 0,
							EdbInfoId:                 0,
							Source:                    edbInfo.Source,
							SourceName:                edbInfo.SourceName,
							EdbCode:                   "",
							FromEdbInfoId:             fromEdbInfo.EdbInfoId,
							FromEdbCode:               fromEdbInfo.EdbCode,
							FromEdbName:               fromEdbInfo.EdbName,
							FromSource:                fromEdbInfo.Source,
							FromSourceName:            fromEdbInfo.SourceName,
							//FromTag:                   tmpEdbInfoId.FromTag,
							Sort:       k + 1,
							CreateTime: time.Now(),
							ModifyTime: time.Now(),
						}
						calculateMappingList = append(calculateMappingList, tmpCalculateMappingItem)
					}
				}
				// 动态环差规则 关系表
				tmpPredictEdbConfCalculateMapping := &models.PredictEdbConfCalculateMapping{
					//PredictEdbConfCalculateMappingId: 0,
					EdbInfoId:      0,
					ConfigId:       0,
					FromEdbInfoId:  fromEdbInfo.EdbInfoId,
					FromEdbCode:    fromEdbInfo.EdbCode,
					FromEdbName:    fromEdbInfo.EdbName,
					FromSource:     fromEdbInfo.Source,
					FromSourceName: fromEdbInfo.SourceName,
					FromTag:        tmpEdbInfoId.FromTag,
					Sort:           k + 1,
					CreateTime:     time.Now(),
					ModifyTime:     time.Now(),
				}
				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
			}

			calculateRuleMap[ruleIndex] = models.CalculateRule{
				TrendsCalculateMappingList: trendsMappingList,
				EdbInfoList:                edbInfoList,
				//EdbInfoIdBytes:             edbInfoIdBytes,
				//Formula:                    formula,
				RuleType:     v.RuleType,
				EndDate:      v.EndDate,
				EdbInfoIdArr: v.EdbInfoIdArr,
			}
		}

		tmpPredictEdbConf := &models.PredictEdbConf{
			PredictEdbInfoId: 0,
			SourceEdbInfoId:  sourceEdbInfoId,
			RuleType:         v.RuleType,
			//FixedValue:       v.Value,
			Value: v.Value,
			//EndDate:    ruleEndDate,
			ModifyTime: time.Now(),
			CreateTime: time.Now(),
			EndNum:     v.EndNum,
		}
		if endDateType == 0 {
			tmpPredictEdbConf.EndDate = ruleEndDate
		}
		//todo 指标最终的截止日期的更新
		edbInfo.EndDate = v.EndDate

		predictEdbConfList = append(predictEdbConfList, tmpPredictEdbConf)
	}
	err, errMsg = models.AddPredictEdb(edbInfo, calculateMappingList, predictEdbConfList, calculateRuleMap)

	return
}

// EditPredictEdbInfo 编辑预测指标
func EditPredictEdbInfo(edbInfoId, classifyId int, edbName, dataDateType string, endDateType int, ruleList []models.RuleConfig, minValue, maxValue float64, lang string) (edbInfo *models.EdbInfo, err error, errMsg string) {
	// 指标信息校验
	{
		edbInfo, err = models.GetEdbInfoById(edbInfoId)
		if err != nil && err.Error() != utils.ErrNoRow() {
			errMsg = "修改失败"
			err = errors.New("获取预测指标失败,Err:" + err.Error())
			return
		}
		if edbInfo == nil {
			errMsg = "找不到该预测指标"
			err = errors.New(errMsg)
			return
		}
		//必须是普通的指标
		if edbInfo.EdbInfoType != 1 {
			errMsg = "指标异常,不是预测指标"
			err = errors.New(errMsg)
			return
		}
	}

	var predictEdbConf *models.PredictEdbConf
	// 指标配置信息校验
	{
		// 查找该预测指标配置
		predictEdbConfList, tmpErr := models.GetPredictEdbConfListById(edbInfo.EdbInfoId)
		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
			errMsg = "修改失败"
			err = errors.New("获取预测指标配置信息失败,Err:" + tmpErr.Error())
			return
		}
		if len(predictEdbConfList) == 0 {
			errMsg = "找不到该预测指标配置"
			err = errors.New(errMsg)
			return
		}
		predictEdbConf = predictEdbConfList[0]
	}

	// 根据指标名称和指标ID校验库中是否还存在其他同名指标
	existEdbName, err := CheckExistByEdbNameAndEdbInfoId(utils.PREDICT_EDB_INFO_TYPE, edbInfoId, edbName, lang)
	if err != nil {
		errMsg = "判断指标名称是否存在失败"
		err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
		return
	}
	if existEdbName {
		errMsg = "指标名称已存在,请重新填写"
		err = errors.New(errMsg)
		return
	}

	if dataDateType == `` {
		dataDateType = `自然日`
	}

	switch lang {
	case utils.EnLangVersion:
		edbInfo.EdbNameEn = edbName
	default:
		edbInfo.EdbName = edbName

	}
	edbInfo.EdbNameSource = edbName
	edbInfo.ClassifyId = classifyId
	edbInfo.MinValue = minValue
	edbInfo.MaxValue = maxValue
	edbInfo.DataDateType = dataDateType
	edbInfo.ModifyTime = time.Now()
	edbInfo.EndDateType = endDateType
	updateEdbInfoCol := []string{"EdbName", "EdbNameEn", "EdbNameSource", "ClassifyId", "EndDate", "MinValue", "MaxValue", "DataDateType", "ModifyTime", "EndDateType"}

	var sourceEdbInfo *models.EdbInfo
	// 来源指标信息校验
	{
		sourceEdbInfo, err = models.GetEdbInfoById(predictEdbConf.SourceEdbInfoId)
		if err != nil && err.Error() != utils.ErrNoRow() {
			errMsg = "新增失败"
			err = errors.New("获取来源指标失败,Err:" + err.Error())
			return
		}
		if sourceEdbInfo == nil {
			errMsg = "找不到该来源指标"
			err = errors.New(errMsg)
			return
		}
		//必须是普通的指标
		if sourceEdbInfo.EdbInfoType != 0 {
			errMsg = "来源指标异常,不是普通的指标"
			err = errors.New(errMsg)
			return
		}
		//if !utils.InArrayByStr([]string{"日度", "周度", "月度", "年度"}, sourceEdbInfo.Frequency) {
		//	errMsg = "预测指标只支持选择日度、周度、月度、年度的指标"
		//	err = errors.New(errMsg)
		//	return
		//}
	}

	// 预测指标配置
	// 关联关系表
	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
	fromEdbMap := make(map[int]int)

	// 源指标关联关系表
	calculateMappingItem := &models.EdbInfoCalculateMapping{
		//EdbInfoCalculateMappingId: 0,
		EdbInfoId:      edbInfoId,
		Source:         edbInfo.Source,
		SourceName:     edbInfo.SourceName,
		EdbCode:        edbInfo.EdbCode,
		FromEdbInfoId:  sourceEdbInfo.EdbInfoId,
		FromEdbCode:    sourceEdbInfo.EdbCode,
		FromEdbName:    sourceEdbInfo.EdbName,
		FromSource:     sourceEdbInfo.Source,
		FromSourceName: sourceEdbInfo.SourceName,
		//FromTag:        "",
		Sort:       1,
		CreateTime: time.Now(),
		ModifyTime: time.Now(),
	}
	fromEdbMap[sourceEdbInfo.EdbInfoId] = sourceEdbInfo.EdbInfoId
	calculateMappingList = append(calculateMappingList, calculateMappingItem)

	// 动态环差 计算列表
	calculateRuleMap := make(map[int]models.CalculateRule, 0)

	// 预测指标配置
	predictEdbConfList := make([]*models.PredictEdbConf, 0)
	for ruleIndex, v := range ruleList {
		var ruleEndDate time.Time
		if endDateType == 0 {
			// 预测指标配置
			ruleEndDate, err = time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
			if err != nil {
				errMsg = "规则配置的截止日期异常,请重新填写"
				err = errors.New(errMsg)
				return
			}
		} else {
			if v.EndNum <= 0 {
				errMsg = "截止期数不正确,请输入大于等于1的整数"
				err = errors.New(errMsg)
				return
			}
		}

		//1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值,9:动态环差,10:根据 给定终值后插值 规则获取预测数据,11:根据 季节性 规则获取预测数据,12:根据 移动平均同比 规则获取预测数据
		// 环比、环差、动态环差、季节性、移动平均同比不支持年度
		if sourceEdbInfo.Frequency == "年度" && utils.InArrayByInt([]int{5, 6, 11, 12}, v.RuleType) {
			errMsg = "环比、环差、动态环差、季节性、移动平均同比不支持年度指标"
			err = errors.New(errMsg)
			return
		}
		if v.RuleType == 16 && endDateType != 1 {
			errMsg = "年度值倒推不支持截止期数"
			err = errors.New(errMsg)
			return
		}
		switch v.RuleType {
		case 8: //N期段线性外推值
			valInt, tmpErr := strconv.Atoi(v.Value)
			if tmpErr != nil {
				errMsg = "N期段线性外推值的N值异常"
				err = errors.New(errMsg)
				return
			}
			if valInt <= 1 {
				errMsg = "N期段线性外推值的N值必须大于1"
				err = errors.New(errMsg)
				return
			}
		case 9: //9:动态环差
			if v.Value == "" {
				errMsg = "请填写计算规则"
				err = errors.New(errMsg)
				return
			}
			formula := v.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)
			v.Value = formula

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

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

			//关联的指标信息
			edbInfoList := make([]*models.EdbInfo, 0)
			// 动态环差规则 关系表
			trendsMappingList := make([]*models.PredictEdbConfCalculateMapping, 0)

			for k, tmpEdbInfoId := range v.EdbInfoIdArr {
				fromEdbInfo, tmpErr := models.GetEdbInfoById(tmpEdbInfoId.EdbInfoId)
				if tmpErr != nil {
					err = tmpErr
					if err.Error() == utils.ErrNoRow() {
						errMsg = "指标 " + strconv.Itoa(tmpEdbInfoId.EdbInfoId) + " 不存在"
						err = errors.New(errMsg)
						return
					}
					errMsg = "获取指标失败:Err:" + err.Error()
					err = errors.New(errMsg)
					return
				}

				edbInfoList = append(edbInfoList, fromEdbInfo)

				//总的 预测指标与所有相关联指标的关系表(不仅仅该条规则)
				{
					if _, ok := fromEdbMap[tmpEdbInfoId.EdbInfoId]; !ok {
						fromEdbMap[tmpEdbInfoId.EdbInfoId] = tmpEdbInfoId.EdbInfoId
						calculateMappingItem := &models.EdbInfoCalculateMapping{
							EdbInfoCalculateMappingId: 0,
							EdbInfoId:                 edbInfoId,
							Source:                    utils.DATA_SOURCE_CALCULATE,
							SourceName:                "指标运算",
							EdbCode:                   "",
							FromEdbInfoId:             fromEdbInfo.EdbInfoId,
							FromEdbCode:               fromEdbInfo.EdbCode,
							FromEdbName:               fromEdbInfo.EdbName,
							FromSource:                fromEdbInfo.Source,
							FromSourceName:            fromEdbInfo.SourceName,
							//FromTag:                   tmpEdbInfoId.FromTag,
							Sort:       k + 1,
							CreateTime: time.Now(),
							ModifyTime: time.Now(),
						}
						calculateMappingList = append(calculateMappingList, calculateMappingItem)
					}
				}
				// 动态环差规则 关系表
				tmpPredictEdbConfCalculateMapping := &models.PredictEdbConfCalculateMapping{
					//PredictEdbConfCalculateMappingId: 0,
					EdbInfoId:      edbInfoId,
					ConfigId:       0,
					FromEdbInfoId:  fromEdbInfo.EdbInfoId,
					FromEdbCode:    fromEdbInfo.EdbCode,
					FromEdbName:    fromEdbInfo.EdbName,
					FromSource:     fromEdbInfo.Source,
					FromSourceName: fromEdbInfo.SourceName,
					FromTag:        tmpEdbInfoId.FromTag,
					Sort:           k + 1,
					CreateTime:     time.Now(),
					ModifyTime:     time.Now(),
				}
				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
			}

			for _, f := range formulaSlice {
				formulaMap, e := utils.CheckFormula(f)
				if e != nil {
					err = fmt.Errorf("公式错误,请重新填写")
					return
				}
				//预先计算,判断公式是否正常
				ok, _ := models.CheckFormula2(edbInfoList, formulaMap, f, edbInfoIdBytes)
				if !ok {
					errMsg = "生成计算指标失败,请使用正确的计算公式"
					err = errors.New(errMsg)
					return
				}
			}
			calculateRuleMap[ruleIndex] = models.CalculateRule{
				TrendsCalculateMappingList: trendsMappingList,
				EdbInfoList:                edbInfoList,
				EdbInfoIdBytes:             edbInfoIdBytes,
				Formula:                    formula,
				RuleType:                   v.RuleType,
				EndDate:                    v.EndDate,
				EdbInfoIdArr:               v.EdbInfoIdArr,
			}
		case 14: //14:根据 一元线性拟合 规则获取预测数据
			if v.Value == "" {
				errMsg = "请填写一元线性拟合规则"
				err = errors.New(errMsg)
				return
			}

			//关联的指标信息
			edbInfoList := make([]*models.EdbInfo, 0)
			// 动态环差规则 关系表
			trendsMappingList := make([]*models.PredictEdbConfCalculateMapping, 0)

			for k, tmpEdbInfoId := range v.EdbInfoIdArr {
				fromEdbInfo, tmpErr := models.GetEdbInfoById(tmpEdbInfoId.EdbInfoId)
				if tmpErr != nil {
					err = tmpErr
					if err.Error() == utils.ErrNoRow() {
						errMsg = "指标 " + strconv.Itoa(tmpEdbInfoId.EdbInfoId) + " 不存在"
						err = errors.New(errMsg)
						return
					}
					errMsg = "获取指标失败:Err:" + err.Error()
					err = errors.New(errMsg)
					return
				}

				edbInfoList = append(edbInfoList, fromEdbInfo)

				//总的 预测指标与所有相关联指标的关系表(不仅仅该条规则)
				{
					if _, ok := fromEdbMap[tmpEdbInfoId.EdbInfoId]; !ok {
						fromEdbMap[tmpEdbInfoId.EdbInfoId] = tmpEdbInfoId.EdbInfoId
						tmpCalculateMappingItem := &models.EdbInfoCalculateMapping{
							EdbInfoCalculateMappingId: 0,
							EdbInfoId:                 0,
							Source:                    utils.DATA_SOURCE_CALCULATE,
							SourceName:                "指标运算",
							EdbCode:                   "",
							FromEdbInfoId:             fromEdbInfo.EdbInfoId,
							FromEdbCode:               fromEdbInfo.EdbCode,
							FromEdbName:               fromEdbInfo.EdbName,
							FromSource:                fromEdbInfo.Source,
							FromSourceName:            fromEdbInfo.SourceName,
							//FromTag:                   tmpEdbInfoId.FromTag,
							Sort:       k + 1,
							CreateTime: time.Now(),
							ModifyTime: time.Now(),
						}
						calculateMappingList = append(calculateMappingList, tmpCalculateMappingItem)
					}
				}
				// 动态环差规则 关系表
				tmpPredictEdbConfCalculateMapping := &models.PredictEdbConfCalculateMapping{
					//PredictEdbConfCalculateMappingId: 0,
					EdbInfoId:      0,
					ConfigId:       0,
					FromEdbInfoId:  fromEdbInfo.EdbInfoId,
					FromEdbCode:    fromEdbInfo.EdbCode,
					FromEdbName:    fromEdbInfo.EdbName,
					FromSource:     fromEdbInfo.Source,
					FromSourceName: fromEdbInfo.SourceName,
					FromTag:        tmpEdbInfoId.FromTag,
					Sort:           k + 1,
					CreateTime:     time.Now(),
					ModifyTime:     time.Now(),
				}
				trendsMappingList = append(trendsMappingList, tmpPredictEdbConfCalculateMapping)
			}
			// todo
			calculateRuleMap[ruleIndex] = models.CalculateRule{
				TrendsCalculateMappingList: trendsMappingList,
				EdbInfoList:                edbInfoList,
				//EdbInfoIdBytes:             edbInfoIdBytes,
				//Formula:                    formula,
				RuleType:     v.RuleType,
				EndDate:      v.EndDate,
				EdbInfoIdArr: v.EdbInfoIdArr,
			}

		}

		tmpPredictEdbConf := &models.PredictEdbConf{
			PredictEdbInfoId: edbInfoId,
			SourceEdbInfoId:  sourceEdbInfo.EdbInfoId,
			RuleType:         v.RuleType,
			//FixedValue:       v.Value,
			Value:      v.Value,
			EndDate:    ruleEndDate,
			EndNum:     v.EndNum,
			ModifyTime: time.Now(),
			CreateTime: time.Now(),
		}
		if endDateType == 0 {
			tmpPredictEdbConf.EndDate = ruleEndDate
		}
		// todo
		edbInfo.EndDate = v.EndDate

		predictEdbConfList = append(predictEdbConfList, tmpPredictEdbConf)
	}

	err, errMsg = models.EditPredictEdb(edbInfo, updateEdbInfoCol, calculateMappingList, predictEdbConfList, calculateRuleMap)

	return
}

// RefreshPredictEdbInfo 更新基础预测指标规则中的动态数据
func RefreshPredictEdbInfo(edbInfoId int) (edbInfo *models.EdbInfo, err error, errMsg string) {
	// 指标信息校验
	{
		edbInfo, err = models.GetEdbInfoById(edbInfoId)
		if err != nil && err.Error() != utils.ErrNoRow() {
			errMsg = "刷新失败"
			err = errors.New("获取预测指标失败,Err:" + err.Error())
			return
		}
		if edbInfo == nil {
			errMsg = "找不到该预测指标"
			err = nil
			return
		}
		//必须是普通的指标
		if edbInfo.EdbInfoType != 1 {
			errMsg = "指标异常,不是预测指标"
			return
		}
	}

	// 配置 与 指标的 关联关系表
	list, err := models.GetPredictEdbConfCalculateMappingListByEdbInfoId(edbInfoId)
	if err != nil {
		return
	}
	// 没有关联指标,不需要刷新
	if len(list) <= 0 {
		return
	}

	// 配置关联的指标信息
	predictEdbConfCalculateMappingListMap := make(map[int][]*models.PredictEdbConfCalculateMapping)
	configIdList := make([]int, 0)       //关联配置id
	edbInfoIdList := make([]int, 0)      //关联指标配置id
	edbInfoIdMap := make(map[int]int, 0) //关联指标配置map
	for _, v := range list {
		configList, ok := predictEdbConfCalculateMappingListMap[v.ConfigId]
		if !ok {
			configList = make([]*models.PredictEdbConfCalculateMapping, 0)
			configIdList = append(configIdList, v.ConfigId)
		}
		if _, ok := edbInfoIdMap[v.FromEdbInfoId]; !ok {
			edbInfoIdList = append(edbInfoIdList, v.FromEdbInfoId)
		}
		configList = append(configList, v)
		predictEdbConfCalculateMappingListMap[v.ConfigId] = configList
	}

	predictEdbConfList, err := models.GetPredictEdbConfListByConfigIdList(configIdList)
	if err != nil {
		errMsg = "刷新失败"
		err = errors.New("获取预测指标配置信息失败,Err:" + err.Error())
		return
	}
	if len(predictEdbConfList) == 0 {
		errMsg = "找不到该预测指标配置"
		err = nil
		return
	}

	// 指标信息
	edbInfoList, err := models.GetEdbInfoByIdList(edbInfoIdList)
	if err != nil {
		err = errors.New("获取关联指标失败,Err:" + err.Error())
		return
	}
	// 指标信息map
	edbInfoListMap := make(map[int]*models.EdbInfo)
	for _, v := range edbInfoList {
		edbInfoListMap[v.EdbInfoId] = v
	}

	predictEdbConfAndDataList := make([]*models.PredictEdbConfAndData, 0)
	// 刷新所有的规则
	for _, v := range predictEdbConfList {
		// 每次规则计算的时候,产生的临时数据
		resultDataList := make([]*models.EdbInfoSearchData, 0)

		switch v.RuleType {
		case 9: //动态环差值
			if v.Value == "" {
				errMsg = "请填写计算规则"
				return
			}
			// todo 动态环差的空值类型处理
			formula := v.Value

			// 动态环差规则 关系表
			trendsMappingList := predictEdbConfCalculateMappingListMap[v.ConfigId]

			// 关联标签
			edbInfoIdArr := make([]models.EdbInfoFromTag, 0)

			//关联的指标信息
			edbInfoList := make([]*models.EdbInfo, 0)
			for _, trendsMapping := range trendsMappingList {
				tmpEdbInfo, ok := edbInfoListMap[trendsMapping.FromEdbInfoId]
				if ok {
					edbInfoList = append(edbInfoList, tmpEdbInfo)
				}

				// 关联标签
				edbInfoIdArr = append(edbInfoIdArr, models.EdbInfoFromTag{
					EdbInfoId: trendsMapping.FromEdbInfoId,
					FromTag:   trendsMapping.FromTag,
				})
			}

			//检验公式
			var formulaStr string
			var edbInfoIdBytes []string
			for _, tmpEdbInfoId := range edbInfoIdArr {
				formulaStr += tmpEdbInfoId.FromTag + ","
				edbInfoIdBytes = append(edbInfoIdBytes, tmpEdbInfoId.FromTag)
			}

			formulaSlice, tErr := utils.CheckFormulaJson(formula)
			if tErr != nil {
				errMsg = "公式格式错误,请重新填写"
				err = errors.New(errMsg)
				return
			}
			for _, fm := range formulaSlice {
				formulaMap, e := utils.CheckFormula(fm)
				if e != nil {
					err = fmt.Errorf("公式错误,请重新填写")
					return
				}
				for _, f := range formulaMap {
					if !strings.Contains(formulaStr, f) {
						errMsg = "公式错误,请重新填写"
						err = errors.New(errMsg)
						return
					}
				}
				//预先计算,判断公式是否正常
				ok, _ := models.CheckFormula2(edbInfoList, formulaMap, fm, edbInfoIdBytes)
				if !ok {
					errMsg = "生成计算指标失败,请使用正确的计算公式"
					return
				}
			}

			rule := models.CalculateRule{
				EdbInfoId:                  v.PredictEdbInfoId,
				ConfigId:                   v.ConfigId,
				TrendsCalculateMappingList: trendsMappingList,
				EdbInfoList:                edbInfoList,
				EdbInfoIdBytes:             edbInfoIdBytes,
				Formula:                    formula,
				RuleType:                   v.RuleType,
				EndDate:                    v.EndDate.Format(utils.FormatDate),
				EdbInfoIdArr:               edbInfoIdArr,
			}
			resultDataList, err = models.RefreshCalculateByRuleBy9(rule)
			if err != nil {
				return
			}
		case 14: //14:根据 一元线性拟合 规则获取预测数据

			if v.Value == "" {
				errMsg = "一元线性拟合规则信息未配置"
				return
			}
			err, errMsg = models.RefreshCalculateByRuleByLineNh(*edbInfo, predictEdbConfAndDataList, *v)
			if err != nil {
				return
			}
		}

		// 规则配置(含数据)
		tmpPredictEdbConfAndData := &models.PredictEdbConfAndData{
			ConfigId:         0,
			PredictEdbInfoId: 0,
			SourceEdbInfoId:  v.SourceEdbInfoId,
			RuleType:         v.RuleType,
			FixedValue:       v.FixedValue,
			Value:            v.Value,
			EndDate:          v.EndDate,
			ModifyTime:       v.ModifyTime,
			CreateTime:       v.CreateTime,
			DataList:         resultDataList,
		}
		predictEdbConfAndDataList = append(predictEdbConfAndDataList, tmpPredictEdbConfAndData)

	}

	return
}

// checkExistByEdbName
// @Description: 根据指标名称校验该指标是否存在库中
// @author: Roc
// @datetime 2024-04-18 14:58:52
// @param edbInfoType int
// @param edbName string
// @param lang string
// @return has bool
// @return err error
func checkExistByEdbName(edbInfoType int, edbName, lang string) (has bool, err error) {
	var condition string
	var pars []interface{}

	condition += " AND edb_info_type=? "
	pars = append(pars, edbInfoType)

	switch lang {
	case utils.EnLangVersion:
		condition += " AND edb_name_en = ? "
	default:
		condition += " AND edb_name=? "
	}

	pars = append(pars, edbName)

	count, err := models.GetEdbInfoCountByCondition(condition, pars)
	if err != nil {
		return
	}

	if count > 0 {
		has = true
		return
	}

	return
}

// checkExistByEdbNameAndEdbInfoId
// @Description: 根据指标名称和指标ID校验库中是否还存在其他同名指标
// @author: Roc
// @datetime 2024-04-18 15:00:19
// @param edbInfoType int
// @param edbInfoId int
// @param edbName string
// @param lang string
// @return has bool
// @return err error
func checkExistByEdbNameAndEdbInfoId(edbInfoType, edbInfoId int, edbName, lang string) (has bool, err error) {
	var condition string
	var pars []interface{}
	condition += " AND edb_info_type=? "
	pars = append(pars, edbInfoType)

	condition += " AND edb_info_id<>? "
	pars = append(pars, edbInfoId)

	switch lang {
	case utils.EnLangVersion:
		condition += " AND edb_name_en = ? "
	default:
		condition += " AND edb_name=? "
	}

	pars = append(pars, edbName)

	count, err := models.GetEdbInfoCountByCondition(condition, pars)
	if err != nil {
		return
	}

	if count > 0 {
		has = true
		return
	}

	return
}

// CheckExistByEdbNameAndEdbInfoId
// @Description: 根据指标名称和指标ID校验库中是否还存在其他同名指标
// @author: Roc
// @datetime 2024-04-18 15:01:44
// @param edbInfoType int
// @param edbInfoId int
// @param edbName string
// @param lang string
// @return has bool
// @return err error
func CheckExistByEdbNameAndEdbInfoId(edbInfoType, edbInfoId int, edbName, lang string) (has bool, err error) {
	// 指标没有入库的情况
	if edbInfoId == 0 {
		return checkExistByEdbName(edbInfoType, edbName, lang)
	}

	//指标已经入库的情况
	return checkExistByEdbNameAndEdbInfoId(edbInfoType, edbInfoId, edbName, lang)
}

// AddStaticPredictEdbInfo 新增静态指标数据
func AddStaticPredictEdbInfo(sourceEdbInfoId, classifyId int, edbName, frequency, unit string, sysUserId int, sysUserName, lang string) (edbInfo *models.EdbInfo, err error, errMsg string) {
	var sourceEdbInfo *models.EdbInfo
	// 来源指标信息校验
	{
		sourceEdbInfo, err = models.GetEdbInfoById(sourceEdbInfoId)
		if err != nil && err.Error() != utils.ErrNoRow() {
			errMsg = "新增失败"
			err = errors.New("获取来源指标失败,Err:" + err.Error())
			return
		}
		if sourceEdbInfo == nil {
			errMsg = "找不到该来源指标"
			err = errors.New(errMsg)
			return
		}
	}

	var classifyInfo *models.EdbClassify
	// 来源分类信息校验
	{
		classifyInfo, err = models.GetEdbClassifyById(classifyId)
		if err != nil && err.Error() != utils.ErrNoRow() {
			errMsg = "新增失败"
			err = errors.New("获取预测指标分类失败,Err:" + err.Error())
			return
		}
		if classifyInfo == nil {
			errMsg = "找不到该预测指标分类"
			err = errors.New(errMsg)
			return
		}
		//必须是预测指标分类
		if classifyInfo.ClassifyType != 1 {
			errMsg = "预测指标分类异常,不是预测指标分类"
			err = errors.New(errMsg)
			return
		}
	}

	edbName = strings.Trim(edbName, " ")

	edbCode := sourceEdbInfo.EdbCode + "_" + time.Now().Format(utils.FormatShortDateTimeUnSpace)

	// 根据指标名称和指标ID校验库中是否还存在其他同名指标
	existEdbName, err := CheckExistByEdbNameAndEdbInfoId(utils.PREDICT_EDB_INFO_TYPE, 0, edbName, lang)
	if err != nil {
		errMsg = "判断指标名称是否存在失败"
		err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
		return
	}
	if existEdbName {
		errMsg = "指标名称已存在,请重新填写"
		err = errors.New(errMsg)
		return
	}

	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
	edbInfo = &models.EdbInfo{
		//EdbInfoId:   0,
		EdbInfoType:      sourceEdbInfo.EdbInfoType,
		SourceName:       sourceEdbInfo.SourceName,
		Source:           sourceEdbInfo.Source,
		EdbCode:          edbCode,
		EdbName:          edbName,
		EdbNameSource:    edbName,
		Frequency:        frequency,
		Unit:             unit,
		StartDate:        sourceEdbInfo.StartDate,
		EndDate:          sourceEdbInfo.EndDate,
		ClassifyId:       classifyId,
		SysUserId:        sysUserId,
		SysUserRealName:  sysUserName,
		UniqueCode:       utils.MD5(utils.DATA_PREFIX + "_" + timestamp),
		CreateTime:       time.Now(),
		ModifyTime:       time.Now(),
		MinValue:         sourceEdbInfo.MinValue,
		MaxValue:         sourceEdbInfo.MaxValue,
		EndValue:         sourceEdbInfo.EndValue,
		CalculateFormula: sourceEdbInfo.CalculateFormula,
		EdbType:          sourceEdbInfo.EdbType,
		//Sort:             sourceEdbInfo.,
		LatestDate:    sourceEdbInfo.LatestDate,
		LatestValue:   sourceEdbInfo.LatestValue,
		MoveType:      sourceEdbInfo.MoveType,
		MoveFrequency: sourceEdbInfo.MoveFrequency,
		NoUpdate:      sourceEdbInfo.NoUpdate,
		IsUpdate:      sourceEdbInfo.IsUpdate,
		ServerUrl:     "",
		EdbNameEn:     edbName,
		UnitEn:        sourceEdbInfo.UnitEn,
		DataDateType:  sourceEdbInfo.DataDateType,
		Sort:          models.GetAddEdbMaxSortByClassifyId(classifyId, utils.PREDICT_EDB_INFO_TYPE),
		IsStaticData:  1,
	}

	// 关联关系表
	calculateMappingList := make([]*models.EdbInfoCalculateMapping, 0)
	fromEdbMap := make(map[int]int)

	// 源指标关联关系表
	calculateMappingItem := &models.EdbInfoCalculateMapping{
		//EdbInfoCalculateMappingId: 0,
		//EdbInfoId:                 0,
		Source:         edbInfo.Source,
		SourceName:     edbInfo.SourceName,
		EdbCode:        edbInfo.EdbCode,
		FromEdbInfoId:  sourceEdbInfo.EdbInfoId,
		FromEdbCode:    sourceEdbInfo.EdbCode,
		FromEdbName:    sourceEdbInfo.EdbName,
		FromSource:     sourceEdbInfo.Source,
		FromSourceName: sourceEdbInfo.SourceName,
		//FromTag:        "",
		Sort:       1,
		CreateTime: time.Now(),
		ModifyTime: time.Now(),
	}
	fromEdbMap[sourceEdbInfoId] = sourceEdbInfoId
	calculateMappingList = append(calculateMappingList, calculateMappingItem)
	newPredictEdbConfList := make([]*models.PredictEdbConf, 0)
	//查询原先的预测指标配置项
	if sourceEdbInfo.EdbType == 1 {
		// 查找该预测指标配置
		predictEdbConfList, tmpErr := models.GetPredictEdbConfListById(sourceEdbInfo.EdbInfoId)
		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
			errMsg = "获取预测指标配置信息失败"
			err = errors.New("获取预测指标配置信息失败,Err:" + tmpErr.Error())
			return
		}
		if len(predictEdbConfList) > 0 {
			// 遍历
			for _, v := range predictEdbConfList {
				tmpPredictEdbConf := &models.PredictEdbConf{
					PredictEdbInfoId: 0,
					SourceEdbInfoId:  sourceEdbInfoId,
					RuleType:         v.RuleType,
					FixedValue:       v.FixedValue,
					Value:            v.Value,
					EmptyType:        v.EmptyType,
					MaxEmptyType:     v.MaxEmptyType,
					EndDate:          v.EndDate,
					ModifyTime:       time.Now(),
					CreateTime:       time.Now(),
				}
				newPredictEdbConfList = append(newPredictEdbConfList, tmpPredictEdbConf)
			}
		}
	}

	err, errMsg = models.AddPredictStaticEdb(edbInfo, sourceEdbInfo, calculateMappingList, newPredictEdbConfList)

	return
}