package logic

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

// ChartInfoDateReq 图表的日期数据（日期相关）
type ChartInfoDateReq struct {
	Type  int    `description:"配置类型"`
	Date  string `description:"固定日期"`
	Value int    `description:"N天的值"`
	Color string `description:"颜色"`
	Name  string `description:"别名"`
}

// SectionScatterReq 截面散点请求
type SectionScatterReq struct {
	XName       string `description:"x轴名称"`
	XNameEn     string `description:"x轴名称(英文)"`
	XUnitName   string `description:"x轴单位名称"`
	XUnitNameEn string `description:"x轴单位名称(英文)"`
	YName       string `description:"y轴名称"`
	YNameEn     string `description:"y轴名称(英文)"`
	YUnitName   string `description:"y轴单位名称"`
	YUnitNameEn string `description:"y轴单位名称(英文)"`
	XMinValue   string `description:"X轴的最小值"`
	XMaxValue   string `description:"X轴的最大值"`
	YMinValue   string `description:"Y轴的最小值"`
	YMaxValue   string `description:"Y轴的最大值"`
	//EdbList     []SectionScatterEdbItemReq    `description:"指标数据"`
	SeriesList []SectionScatterSeriesItemReq `description:"系列数据"`
}

// SectionScatterSeriesItemReq 系列的请求
type SectionScatterSeriesItemReq struct {
	Name            string `description:"系列名"`
	NameEn          string `description:"系列名(英文名)"`
	Color           string `description:"颜色"`
	EdbInfoList     []SectionScatterEdbItemReq
	ShowTrendLine   bool `description:"是否展示趋势线"`
	ShowFitEquation bool `description:"是否展示方程式"`
	ShowRSquare     bool `description:"是否展示R平方"`
}

// SectionScatterEdbItemReq 截面散点请求的指标
type SectionScatterEdbItemReq struct {
	XEdbInfoId int    `description:"X轴的指标ID"`
	YEdbInfoId int    `description:"Y轴的指标ID"`
	Name       string `description:"别名"`
	NameEn     string `description:"英文别名"`
	XDateType  int    `description:"X轴的日期配置类型"`
	XDate      string `description:"X轴的日期固定日期"`
	XDateValue int    `description:"X轴的日期N天的值"`
	YDateType  int    `description:"Y轴的日期配置类型"`
	YDate      string `description:"Y轴的日期固定日期"`
	YDateValue int    `description:"Y轴的日期N天的值"`
	IsShow     bool   `description:"是否展示"`
}

// XData 商品价格曲线的的x轴数据
type XData struct {
	Name   string `description:"别名"`
	NameEn string `description:"英文别名"`
}

// YData 柱方图的y轴数据
type YData struct {
	Date           string          `description:"数据日期"`
	ConfigDate     time.Time       `description:"配置的日期" json:"-"`
	Color          string          `description:"数据颜色"`
	Name           string          `description:"别名"`
	NameEn         string          `description:"英文别名"`
	Value          []float64       `description:"每个指标的值"`
	NoDataEdbList  []int           `description:"没有数据的指标列表"`
	XEdbInfoIdList []int           `description:"对应X轴的指标id列表"`
	NameList       []string        `description:"每个值对应的名称"`
	EnNameList     []string        `description:"每个值对应的英文名称"`
	EdbValMap      map[int]float64 `description:"指标与值的对应" json:"-"`
	M              []int           `description:"对应开始日期的间隔值" json:"-"`
}

// BarChartInfoEdbItemReq 柱方图预览请求数据（指标相关）
type BarChartInfoEdbItemReq struct {
	EdbInfoId int    `description:"指标ID"`
	Name      string `description:"别名"`
	NameEn    string `description:"英文别名"`
	Source    int    `description:"1：ETA图库；2：商品价格"`
}

// ChartInfoReq 图表预览请求数据
type ChartInfoReq struct {
	FutureGoodEdbInfoIdList []models.EdbInfoFromTag `description:"指标信息"`
	CalculateFormula        string                  `description:"计算公式"`
	BaseEdbInfoId           int                     `description:"基础的指标id"`
	DateList                []ChartInfoDateReq      `description:"日期配置"`
	ProfitNameEn            string                  `description:"利润英文名称"`
}

// RefreshByChartId 根据图表id刷新图表
func RefreshByChartId(chartInfoId int) (err error, errMsg string) {
	// 查找图表
	chartInfo, err := models.GetChartInfoById(chartInfoId)
	if err != nil {
		if err.Error() == utils.ErrNoRow() {
			err = nil
		}
		return
	}
	if chartInfo.Source != utils.CHART_SOURCE_FUTURE_GOOD_PROFIT {
		return
	}
	var extraConf ChartInfoReq
	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConf)
	if err != nil {
		errMsg = "商品利润曲线图配置异常"
		return
	}

	// 查找商品利润图表的扩展信息
	chartInfoFutureGoodProfit := new(future_good.ChartInfoFutureGoodProfit)
	if err = chartInfoFutureGoodProfit.GetItemById(chartInfoId); err != nil {
		errMsg = "获取失败"
		if err.Error() == utils.ErrNoRow() {
			err = nil
		}
		return
	}

	baseEdbInfo, err := models.GetEdbInfoById(extraConf.BaseEdbInfoId)
	if err != nil {
		errMsg = "获取失败"
		return
	}
	// 商品数据库指标
	futureGoodEdbInfoMap := make(map[int]*future_good.FutureGoodEdbInfo)
	zlFutureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
	for _, v := range extraConf.FutureGoodEdbInfoIdList {
		if _, ok := futureGoodEdbInfoMap[v.EdbInfoId]; ok {
			continue
		}

		zlFutureGoodEdbInfo, tmpErr := future_good.GetFutureGoodEdbInfo(v.EdbInfoId)
		if tmpErr != nil {
			err = tmpErr
			errMsg = "获取失败"
			return
		}

		futureGoodEdbInfoMap[v.EdbInfoId] = zlFutureGoodEdbInfo
		zlFutureGoodEdbInfoList = append(zlFutureGoodEdbInfoList, zlFutureGoodEdbInfo)
	}

	xDataList, yDataList, err := GetProfitChartEdbData(baseEdbInfo, zlFutureGoodEdbInfoList, extraConf.DateList, extraConf.CalculateFormula, extraConf.FutureGoodEdbInfoIdList)

	xDataListByte, err := json.Marshal(xDataList)
	if err != nil {
		errMsg = "保存失败"
		err = errors.New("X轴数据转换失败，ERR：" + err.Error())
		return
	}
	yDataListByte, err := json.Marshal(yDataList)
	if err != nil {
		errMsg = "保存失败"
		err = errors.New("Y轴数据转换失败，ERR：" + err.Error())
		return
	}

	extraUpdateCol := make([]string, 0)
	chartInfoFutureGoodProfit.XValue = string(xDataListByte)
	chartInfoFutureGoodProfit.YValue = string(yDataListByte)
	chartInfoFutureGoodProfit.ProfitName = zlFutureGoodEdbInfoList[0].FutureGoodEdbName + "盘面利润"
	chartInfoFutureGoodProfit.ModifyTime = time.Now()
	extraUpdateCol = []string{"XValue", "YValue", "ProfitName", "ModifyTime"}
	err = chartInfoFutureGoodProfit.Update(extraUpdateCol)

	return
}

// GetProfitChartEdbData 获取利润图表的指标数据
func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []ChartInfoDateReq, formulaStr string, edbInfoFromTagList []models.EdbInfoFromTag) (xDataList []XData, yDataList []YData, err error) {
	if baseEdbInfo == nil {
		err = errors.New("ETA指标未选取")
		return
	}
	if len(zlFutureGoodEdbInfoList) <= 0 {
		err = errors.New("商品指标未选取")
		return
	}

	// 标签与期货商品指标的关联关系
	tagEdbIdMap := make(map[string]int)
	// 有效的期货商品指标
	futureGoodEdbInfoIdMap := make(map[int]int)
	{
		tmpTagEdbIdMap := make(map[string]int)
		for _, v := range edbInfoFromTagList {
			tmpTagEdbIdMap[v.FromTag] = v.EdbInfoId
		}
		formulaMap := CheckFormula(formulaStr)
		for _, tag := range formulaMap {
			tagEdbIdMap[tag] = tmpTagEdbIdMap[tag]
			futureGoodEdbInfoIdMap[tmpTagEdbIdMap[tag]] = tmpTagEdbIdMap[tag]
		}
	}

	// 普通的指标数据
	baseDataList := make([]*models.EdbDataList, 0)
	baseDataList, err = models.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.SubSource, baseEdbInfo.EdbInfoId, "", "")
	if err != nil {
		return
	}

	latestDate := zlFutureGoodEdbInfoList[0].EndDate
	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)

	earliestDateTime := latestDateTime // 数据的最早日期，目的是为了找出最早的合约
	for _, barChartInfoDate := range chartInfoDateList {
		var findDateTime time.Time
		switch barChartInfoDate.Type {
		case 1: //最新值
			findDateTime = latestDateTime
		case 2: //近期几天
			findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
		case 3: // 固定日期
			//寻找固定日期的数据
			tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			findDateTime = tmpFindDateTime
		default:
			err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
			return
		}
		if findDateTime.IsZero() {
			err = errors.New("错误的日期")
			return
		}
		if findDateTime.Before(earliestDateTime) {
			earliestDateTime = findDateTime
		}
	}

	monthNum := (latestDateTime.Year()-earliestDateTime.Year())*12 + int(latestDateTime.Month()-earliestDateTime.Month())

	// 存储主力合约下的所有月份合约
	futureGoodEdbInfoDateMap := make(map[int]map[string]*future_good.FutureGoodEdbInfo)
	futureGoodDataListMap := make(map[int][]*models.EdbDataList, 0)
	// 特殊的商品期货合约（只有M+N的合约，没有固定日期的合约）
	specialFutureGoodEdbInfoMap := make(map[int]map[int]*future_good.FutureGoodEdbInfo, 0)

	isAllChina := true // 是否都是国内的期货合约
	for _, v := range zlFutureGoodEdbInfoList {
		if v.RegionType != "国内" {
			isAllChina = false
			break
		}
	}

	nMap := make(map[string]string)
	var maxN int // 最大N值
	for _, v := range zlFutureGoodEdbInfoList {
		// 如果不是有效的商品期货指标，那么就过滤掉，不做数据查询处理，避免没必要的请求
		if _, ok := futureGoodEdbInfoIdMap[v.FutureGoodEdbInfoId]; !ok {
			continue
		}
		// 获取期货指标以及期货数据
		tmpFutureGoodEdbInfoList, tmpErr := future_good.GetChildFutureGoodEdbInfoListByParentId(v.FutureGoodEdbInfoId)
		if tmpErr != nil {
			err = tmpErr
			return
		}

		childFutureGoodEdbInfoMap, tmpMaxN, tmpErr := getProfitFutureGoodEdbInfoList(earliestDateTime, v, tmpFutureGoodEdbInfoList, isAllChina, monthNum)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		if maxN < tmpMaxN {
			maxN = tmpMaxN
		}
		futureGoodEdbInfoDateMap[v.FutureGoodEdbInfoId] = childFutureGoodEdbInfoMap

		if v.FutureGoodEdbType == 2 {
			specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId] = make(map[int]*future_good.FutureGoodEdbInfo)
		}
		// 获取数据
		for date, childFutureGoodEdbInfo := range childFutureGoodEdbInfoMap {
			nMap[date] = date
			dataList := make([]*models.EdbDataList, 0)

			tmpDataList, tmpErr := future_good.GetFutureGoodEdbDataListByDate(childFutureGoodEdbInfo.FutureGoodEdbInfoId, "", "")
			if tmpErr != nil {
				return
			}
			for _, tmpData := range tmpDataList {
				dataList = append(dataList, &models.EdbDataList{
					EdbDataId:     tmpData.FutureGoodEdbDataId,
					EdbInfoId:     tmpData.FutureGoodEdbInfoId,
					DataTime:      tmpData.DataTime.Format(utils.FormatDate),
					DataTimestamp: tmpData.DataTimestamp,
					Value:         tmpData.Close,
				})
			}
			futureGoodDataListMap[childFutureGoodEdbInfo.FutureGoodEdbInfoId] = dataList

			if childFutureGoodEdbInfo.FutureGoodEdbType == 2 {
				specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId][childFutureGoodEdbInfo.Month] = childFutureGoodEdbInfo
			}
		}
	}

	// 找出所有的N值，并进行正序排列
	dateList := make([]string, 0)
	for _, n := range nMap {
		dateList = append(dateList, n)
	}
	sort.Slice(dateList, func(i, j int) bool {
		return dateList[i] < dateList[j]
	})

	_, yDataList, err = ProfitChartChartData(baseDataList, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN)
	if err != nil {
		return
	}

	tmpXDataList, newYDataList, err := handleProfitResultData(baseEdbInfo, yDataList, earliestDateTime)
	if err != nil {
		return
	}
	xDataList = []XData{
		{
			Name:   "现货利润",
			NameEn: "Spot Price",
		},
	}
	xDataList = append(xDataList, tmpXDataList...)
	yDataList = newYDataList

	return
}

// ProfitChartChartData 获取数据
func ProfitChartChartData(baseDataList []*models.EdbDataList, futureGoodEdbInfoMap map[int]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*models.EdbDataList, chartInfoDateList []ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN int) (edbIdList []int, yDataList []YData, err error) {
	// 指标数据数组（10086:{"2022-12-02":100.01,"2022-12-01":102.3}）
	//earliestDateTime time.Time
	// ETA指标数据
	baseEdbDateData := make(map[string]float64)
	for _, edbData := range baseDataList {
		baseEdbDateData[edbData.DataTime] = edbData.Value
	}

	// 商品指标数据
	edbDataMap := make(map[int]map[string]float64)
	for edbInfoId, edbDataList := range futureGoodEdbDataListMap {
		edbDateData := make(map[string]float64)
		for _, edbData := range edbDataList {
			edbDateData[edbData.DataTime] = edbData.Value
		}
		edbDataMap[edbInfoId] = edbDateData
	}

	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)

	yDataList = make([]YData, 0) //y轴的数据列表

	// 将计算公式中的字母转大写
	formulaStr = strings.ToUpper(formulaStr)

	for _, barChartInfoDate := range chartInfoDateList {
		yDataMap := make(map[int]float64)
		var maxDate time.Time

		var findDateTime time.Time
		switch barChartInfoDate.Type {
		case 1: //最新值
			findDateTime = latestDateTime
		case 2: //近期几天
			findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
		case 3: // 固定日期
			//寻找固定日期的数据
			tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			findDateTime = tmpFindDateTime
		default:
			err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
			return
		}
		if findDateTime.IsZero() {
			err = errors.New("错误的日期")
			return
		}

		findDataList := make([]float64, 0) // 当前日期的数据值
		noDataIdList := make([]int, 0)     // 没有数据的指标id
		xEdbInfoIdList := make([]int, 0)   // 当前数据的指标id列表

		// 现货指标
		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataList, baseEdbDateData, edbDataMap)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		findDataList = append(findDataList, findDataValue)
		yDataMap[0] = findDataValue
		if isFind {
			maxDate = realDateTime
		}

		xEdbInfoIdList = append(xEdbInfoIdList, 0)

		mList := make([]int, 0) // 间隔月份

		// 最小开始的n值
		//minN := (findDateTime.Year()-earliestDateTime.Year())*12 + int(findDateTime.Month()-earliestDateTime.Month())
		for _, date := range dateList {
			currDate, _ := time.ParseInLocation(utils.FormatYearMonthDate, date, time.Local)
			// 如果当前的n值小于最小开始的n值，那么就不处理
			//if n < minN {
			//	continue
			//}
			//findDateTime

			// 获取当前日期相对开始日期的期数
			//tmpN := (currDate.Year()-findDateTime.Year())*12 + int(currDate.Month()-findDateTime.Month())
			// 用实际日期的月份作为基准，往前推12个月（2024-5-13 16:26:43修改）
			tmpN := (currDate.Year()-realDateTime.Year())*12 + int(currDate.Month()-realDateTime.Month())
			if tmpN <= 0 {
				continue
			}

			// 如果期数大于最大期数，那么就退出当前匹配
			if tmpN >= maxN {
				break
			}

			zlAndChildEdbId := make(map[int]int)
			childFutureGoodEdbInfoIdList := make([]int, 0)
			for zlFutureGoodEdbInfoId, futureGoodEdbInfoList := range futureGoodEdbInfoMap {
				// 判断是否特殊合约
				if childFutureGoodEdbInfoIdMap, ok := specialFutureGoodEdbInfoMap[zlFutureGoodEdbInfoId]; ok {
					if childFutureGoodEdbInfo, ok2 := childFutureGoodEdbInfoIdMap[tmpN]; ok2 {
						childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
						zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
					}

				} else {
					if childFutureGoodEdbInfo, ok2 := futureGoodEdbInfoList[date]; ok2 {
						childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
						zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
					}
				}
			}

			// 合约不全，不参与计算
			//if len(childFutureGoodEdbInfoIdList) != lenZlFutureGoodEdbInfo {
			//	continue
			//}

			calculateMap := make(map[int]float64)
			for _, childFutureGoodEdbInfoId := range childFutureGoodEdbInfoIdList {
				tmpFindDataValue, tmpIsFind := edbDataMap[childFutureGoodEdbInfoId][realDateTime.Format(utils.FormatDate)]

				if tmpIsFind && tmpFindDataValue != 0 {
					calculateMap[childFutureGoodEdbInfoId] = tmpFindDataValue
				}
			}
			// 合约的数据不全，不参与计算
			//if len(calculateMap) != lenZlFutureGoodEdbInfo {
			//	continue
			//}

			newTagEdbIdMap := make(map[string]int)
			for tag, zlEdbId := range tagEdbIdMap {
				newTagEdbIdMap[tag] = zlAndChildEdbId[zlEdbId]
			}
			//, formulaStr string, tagEdbIdMap map[string]int
			formulaFormStr := ReplaceFormula(newTagEdbIdMap, calculateMap, formulaStr)
			//计算公式异常，那么就移除该指标
			if formulaFormStr == `` {
				//removeDateList = append(removeDateList, sk)
				//fmt.Println("异常了")
				continue
			}

			expression := formula.NewExpression(formulaFormStr)
			calResult, evaluateErr := expression.Evaluate()
			if evaluateErr != nil {
				// 分母为0的报错
				if strings.Contains(evaluateErr.Error(), "divide by zero") {
					//removeDateList = append(removeDateList, sk)
					continue
				}
				err = errors.New("计算失败：Err:" + evaluateErr.Error() + ";formulaStr:" + formulaFormStr)
				fmt.Println(err)
				continue
			}
			// 如果计算结果是NAN，那么就退出当前循环
			if calResult.IsNan() {
				continue
			}
			calVal, tmpErr := calResult.Float64()
			if tmpErr != nil {
				err = errors.New("计算失败：获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
				fmt.Println(err)
				continue
			}
			//yDataMap[n] = calVal
			//xEdbInfoIdList = append(xEdbInfoIdList, n)
			nanCheck := fmt.Sprintf("%0.f", calVal)
			// 分母为0.0的报错
			if nanCheck == "NaN" || nanCheck == "+Inf" || nanCheck == "-Inf" {
				continue
			}
			calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
			yDataMap[tmpN] = calVal
			xEdbInfoIdList = append(xEdbInfoIdList, tmpN)
			findDataList = append(findDataList, calVal)
		}
		yName := barChartInfoDate.Name
		yNameEn := barChartInfoDate.Name
		if yName == `` {
			if barChartInfoDate.Type == 2 {
				yName = strconv.Itoa(barChartInfoDate.Value) + "天前"
				if barChartInfoDate.Value == 1 {
					yNameEn = strconv.Itoa(barChartInfoDate.Value) + "day ago"
				} else {
					yNameEn = strconv.Itoa(barChartInfoDate.Value) + " days ago"
				}
			} else {
				yName = maxDate.Format(utils.FormatDate)
				yNameEn = maxDate.Format(utils.FormatDate)
			}
		}
		yDate := "0000-00-00"
		if !maxDate.IsZero() {
			yDate = maxDate.Format(utils.FormatDate)
		}

		yDataList = append(yDataList, YData{
			Date:           yDate,
			ConfigDate:     realDateTime,
			Value:          findDataList,
			NoDataEdbList:  noDataIdList,
			XEdbInfoIdList: xEdbInfoIdList,
			Color:          barChartInfoDate.Color,
			Name:           yName,
			NameEn:         yNameEn,
			EdbValMap:      yDataMap,
			M:              mList,
		})
	}

	return
}

// getFutureGoodEdbInfoList 获取适用的指标列表
func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbInfo *future_good.FutureGoodEdbInfo, tmpFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, isAllChina bool, monthNum int) (futureGoodEdbInfoDateMap map[string]*future_good.FutureGoodEdbInfo, newMaxN int, err error) {
	maxN := 36 //最大36期合约
	futureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
	futureGoodEdbInfoDateMap = make(map[string]*future_good.FutureGoodEdbInfo)

	earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
	if zlFutureGoodEdbInfo.RegionType == "国内" {
		startMonth := int(earliestDateTime.Month())
		if startMonth == 1 {
			futureGoodEdbInfoList = tmpFutureGoodEdbInfoList
		} else {
			// 因为是下标，所以对应想下标是需要-1的
			index := startMonth - 1
			futureGoodEdbInfoList = tmpFutureGoodEdbInfoList[index:]
			futureGoodEdbInfoList = append(futureGoodEdbInfoList, tmpFutureGoodEdbInfoList[:index]...)
		}

		lenFutureGoodEdbInfoList := len(futureGoodEdbInfoList)
		//futureGoodEdbInfoList
		//if isAllChina {
		//	// 如果全是国内指标，那么只需要拼上多出的几期合约即可
		//	maxN = lenFutureGoodEdbInfoList + monthNum
		//}
		// 如果全是国内指标，那么只需要拼上多出的几期合约即可
		maxN = lenFutureGoodEdbInfoList + monthNum

		for i := 1; i < maxN; i++ {
			k := i % lenFutureGoodEdbInfoList
			futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, i, 0).Format(utils.FormatYearMonthDate)] = futureGoodEdbInfoList[k]
			if i > newMaxN {
				newMaxN = i
			}
		}

		//需求池604，只要是国内合约，最大必须是12期
		newMaxN = 12

		return
	}

	for _, v := range tmpFutureGoodEdbInfoList {
		//海外的连续日期,目前
		if v.FutureGoodEdbType == 2 {
			if v.Month <= newMaxN {
				futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, v.Month, 0).Format(utils.FormatYearMonthDate)] = v
				if v.Month > newMaxN {
					newMaxN = v.Month
				}
			}
			continue
		}

		if v.Year < earliestDateTime.Year() {
			continue
		}
		// 小于等于当前年，那么就肯定是ok的
		if v.Year == earliestDateTime.Year() && v.Month <= int(earliestDateTime.Month()) {
			continue
		}

		subYear := v.Year - earliestDateTime.Year()
		subMonth := v.Month - int(earliestDateTime.Month())
		// 如果（当前年-最新日期的年份） * 12个月 + (当前月-最新日期的月份) 小于总月份
		tmpN := subYear*12 + subMonth
		if tmpN < newMaxN {
			tmpDateTime := time.Date(v.Year, time.Month(v.Month), 0, 0, 0, 0, 0, time.Local)
			futureGoodEdbInfoDateMap[tmpDateTime.Format(utils.FormatYearMonthDate)] = v
			if tmpN > newMaxN {
				newMaxN = tmpN
			}
			continue
		}
	}

	return
}

// handleProfitResultData 处理成最终的结果数据
func handleProfitResultData(baseEdbInfo *models.EdbInfo, yDataList []YData, earliestDateTime time.Time) (xDataList []XData, newYDataList []YData, err error) {
	xDataList = make([]XData, 0)
	newYDataList = yDataList

	nMap := make(map[int]int)

	for _, v := range yDataList {
		for _, n := range v.XEdbInfoIdList {
			nMap[n] = n
		}
	}

	// 找出所有的N值，并进行正序排列
	nList := make([]int, 0)
	for _, n := range nMap {
		nList = append(nList, n)
	}
	sort.Slice(nList, func(i, j int) bool {
		return nList[i] < nList[j]
	})

	for _, n := range nList {
		if n == 0 {
			continue
		}
		xDataList = append(xDataList, XData{
			Name:   fmt.Sprint("M+", n),
			NameEn: fmt.Sprint("M+", n),
		})
	}

	for yIndex, yData := range yDataList {
		newYDataList[yIndex].XEdbInfoIdList = []int{}
		newYDataList[yIndex].Value = []float64{}
		tmpNList := nList

		xEdbInfoIdList := yData.XEdbInfoIdList
		valIndex := 0
		needNum := 0
		for _, n := range tmpNList {
			if len(xEdbInfoIdList) > 0 {
				currN := xEdbInfoIdList[0]
				// 当前距离最早的日期相差的N数
				if n == currN {
					if needNum > 0 {
						currVal := yData.Value[valIndex]
						preVal := yData.Value[valIndex-1]
						hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(needNum + 1)))

						for tmpNum := 0; tmpNum < needNum; tmpNum++ {
							newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, 0)

							// 赋值平均值
							tmpVal, _ := decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(int64(tmpNum + 1)))).RoundCeil(4).Float64()
							newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, tmpVal)
						}
					}

					newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
					newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[valIndex])
					valIndex++
					needNum = 0
					if len(xEdbInfoIdList) > 0 {
						xEdbInfoIdList = xEdbInfoIdList[1:]
					}
				} else {
					needNum++
				}
			}
		}
	}

	maxI := 0
	for _, yData := range newYDataList {
		lenEdb := len(yData.XEdbInfoIdList)
		for i := 0; i < lenEdb; i++ {
			if yData.XEdbInfoIdList[i] != 0 && !utils.InArrayByInt(yData.NoDataEdbList, yData.XEdbInfoIdList[i]) {
				if maxI < i {
					maxI = i
				}
			}
		}
	}

	earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
	xDataList = xDataList[0:maxI]
	for yIndex, yData := range newYDataList {
		if len(yData.XEdbInfoIdList) > maxI+1 {
			newYDataList[yIndex].XEdbInfoIdList = yData.XEdbInfoIdList[0 : maxI+1]
		}
		if len(yData.Value) > maxI+1 {
			newYDataList[yIndex].Value = yData.Value[0 : maxI+1]
		}

		currDate, _ := time.ParseInLocation(utils.FormatDate, yData.Date, time.Local)

		nameList := make([]string, 0)
		enNameList := make([]string, 0)
		for _, n := range newYDataList[yIndex].XEdbInfoIdList {
			if n == 1 { // 现货价不处理
				nameList = append(nameList, baseEdbInfo.EdbName)
				enNameList = append(enNameList, baseEdbInfo.EdbNameEn)
				continue
			}
			if n <= 0 {
				nameList = append(nameList, `无合约`)
				enNameList = append(enNameList, `no contract`)
			} else {
				var date string

				currDateTime := currDate.AddDate(0, n-1, 0)
				month := int(currDateTime.Month())
				date = fmt.Sprintf("%d%d", currDateTime.Year(), month)
				if month < 10 {
					date = fmt.Sprintf("%d0%d", currDateTime.Year(), month)
				}
				nameList = append(nameList, date)
				enNameList = append(enNameList, date)
			}
		}
		newYDataList[yIndex].NameList = nameList
		newYDataList[yIndex].EnNameList = enNameList
	}
	return
}

func CheckFormula(formula string) map[string]string {
	mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG", "LN"}

	str := strings.ToUpper(formula)
	for _, v := range mathFormula {
		str = strings.Replace(str, v, "", -1)
	}
	str = strings.Replace(str, "(", "", -1)
	str = strings.Replace(str, ")", "", -1)

	byteMap := make(map[string]string)
	for i := 0; i < len(str); i++ {
		byteInt := str[i]
		if byteInt >= 65 && byteInt <= 90 {
			byteStr := string(byteInt)
			if _, ok := byteMap[byteStr]; !ok {
				byteMap[byteStr] = byteStr
			}
		}
	}
	return byteMap
}

func ReplaceFormula(tagEdbIdMap map[string]int, valArr map[int]float64, formulaStr string) string {
	funMap := GetFormulaMap()
	for k, v := range funMap {
		formulaStr = strings.Replace(formulaStr, k, v, -1)
	}

	replaceCount := 0
	for tag, edbInfoId := range tagEdbIdMap {
		if val, valOk := valArr[edbInfoId]; valOk { //值存在
			dvStr := fmt.Sprintf("%v", val)
			formulaStr = strings.Replace(formulaStr, tag, dvStr, -1)
			replaceCount++
		}
	}
	for k, v := range funMap {
		formulaStr = strings.Replace(formulaStr, v, k, -1)
	}
	if replaceCount == len(tagEdbIdMap) {
		return formulaStr
	} else {
		return ""
	}
}

func GetFormulaMap() map[string]string {
	funMap := make(map[string]string)
	funMap["MAX"] = "[@@]"
	funMap["MIN"] = "[@!]"
	funMap["ABS"] = "[@#]"
	funMap["CEIL"] = "[@$]"
	funMap["COS"] = "[@%]"
	funMap["FLOOR"] = "[@^]"
	funMap["MOD"] = "[@&]"
	funMap["POW"] = "[@*]"
	funMap["ROUND"] = "[@(]"
	return funMap
}

// GetNeedDateData 获取合约内需要的日期数据
func GetNeedDateData(needDateTime time.Time, dataList []*models.EdbDataList, edbDataMap map[string]float64, allEdbDataMap map[int]map[string]float64) (findDateTime time.Time, findDataValue float64, isFind bool, err error) {
	//dataList := edbDataListMap[edbInfoId] //指标的所有数据值
	if len(dataList) <= 0 {
		// 没有数据的指标id
		return
	}

	//最早的日期
	minDateTime, err := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
	if err != nil {
		return
	}

	// 该日期存在数据的期货指标的最小数量，目前是现货和期货各1个，总共2个
	maxCount := 1

	for tmpDateTime := needDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
		tmpDate := tmpDateTime.Format(utils.FormatDate)
		tmpValue, ok := edbDataMap[tmpDate]
		if !ok {
			continue
		}

		// 该日期存在数据的指标数量
		count := 0

		for _, currEdbDataMap := range allEdbDataMap {
			_, tmpIsFind := currEdbDataMap[tmpDate]
			if tmpIsFind {
				count++
				if count >= maxCount {
					continue
				}
			}
		}

		// 该日期存在数据的期货指标数量小于2个，那么要继续往前找
		if count < maxCount {
			continue
		}

		//如果能找到数据，那么就返回
		// 数据为0,也直接返回,做无值处理
		if tmpValue == 0 {
			return
		}
		findDateTime, _ = time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
		findDataValue = tmpValue
		isFind = true
		return
	}

	return
}
