package cross_variety

import (
	"encoding/json"
	"errors"
	"eta/eta_api/models/data_manage"
	cross_varietyModel "eta/eta_api/models/data_manage/cross_variety"
	"eta/eta_api/models/data_manage/cross_variety/request"
	"eta/eta_api/models/system"
	"eta/eta_api/services/data"
	"eta/eta_api/utils"
	"fmt"
	"github.com/shopspring/decimal"
	"strconv"
	"strings"
	"time"
)

// ChartInfoResp 截面散点图数据
type ChartInfoResp 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轴的最大值"`
	DataList    []SectionScatterSeriesItemResp `description:"数据列"`
}

// SectionScatterSeriesItemResp 系列的返回
type SectionScatterSeriesItemResp struct {
	Name                string            `description:"系列名"`
	NameEn              string            `description:"系列名(英文)"`
	Color               string            `description:"颜色"`
	CoordinatePointData []CoordinatePoint `description:"趋势线的前后坐标点"`
}

// SectionScatterEdbItemResp 截面散点的返回参数
type SectionScatterEdbItemResp struct {
	XEdbInfoId int     `description:"X轴指标id"`
	XDate      string  `description:"X轴指标实际日期"`
	XName      string  `description:"X轴指标名称"`
	XNameEn    string  `description:"X轴指标英文名称"`
	XValue     float64 `description:"X轴实际值"`
	YEdbInfoId int     `description:"Y轴指标id"`
	YDate      string  `description:"Y轴指标实际日期"`
	YName      string  `description:"Y轴指标名称"`
	YNameEn    string  `description:"Y轴指标英文名称"`
	YValue     float64 `description:"Y轴实际值"`
	IsShow     bool    `description:"是否展示"`
	Name       string  `description:"标签名称"`
	NameEn     string  `description:"英文标签名称"`
}

// CoordinatePoint 坐标点
type CoordinatePoint struct {
	X          float64
	Y          float64
	XEdbInfoId int
	YEdbInfoId int
	XDate      string
	YDate      string
	DateType   int `description:"日期类型：1-最新日期；2-N天前；3-固定日期"`
	DaysAgo    int `description:"N天前的N值"`
	ShowTips   int `description:"是否显示标注：0-否；1-是"`
}

// GetChartData
// @Description: 获取跨品种分析图表数据
// @author: Roc
// @datetime 2023-11-24 09:42:59
// @param chartInfoId int
// @param config request.ChartConfigReq
// @return edbList []*data_manage.ChartEdbInfoMapping
// @return dataResp ChartInfoResp
// @return err error
// @return errMsg string
// @return isSendEmail bool
func GetChartData(chartInfoId int, config request.ChartConfigReq) (edbList []*data_manage.ChartEdbInfoMapping, dataResp ChartInfoResp, err error, errMsg string, isSendEmail bool) {
	moveUnitDays, ok := utils.FrequencyDaysMap[config.CalculateUnit]
	if !ok {
		errMsg = "错误的分析周期"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}

	isSendEmail = true

	// 品种map
	varietyMap := make(map[int]*cross_varietyModel.ChartVariety)
	{
		varietyList, tmpErr := cross_varietyModel.GetVarietyListByIdList(config.VarietyList)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		for _, v := range varietyList {
			varietyMap[v.ChartVarietyId] = v
		}
	}

	// 标签m
	var xTagInfo, yTagInfo *cross_varietyModel.ChartTag
	{
		tagList, tmpErr := cross_varietyModel.GetTagListByIdList([]int{config.TagX, config.TagY})
		if tmpErr != nil {
			err = tmpErr
			return
		}
		for _, v := range tagList {
			if v.ChartTagId == config.TagX {
				xTagInfo = v
			} else if v.ChartTagId == config.TagY {
				yTagInfo = v
			}
		}
	}
	if xTagInfo == nil {
		errMsg = "找不到对应的X轴标签"
		err = errors.New(errMsg)
		return
	}
	if yTagInfo == nil {
		errMsg = "找不到对应的Y轴标签"
		err = errors.New(errMsg)
		return
	}

	xVarietyEdbMap, yVarietyEdbMap, edbInfoIdList, err := GetXYEdbIdList(config.TagX, config.TagY, config.VarietyList)
	if err != nil {
		return
	}

	if len(edbInfoIdList) <= 0 {
		errMsg = "品种未配置指标"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	mappingList, err := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
	if err != nil {
		errMsg = "获取指标信息失败"
		err = errors.New("获取指标信息失败，ERR：" + err.Error())
		return
	}

	// 指标对应的所有数据
	chartType := 1 //1：普通图，2：季节性图
	calendar := "公历"
	edbDataListMap, edbList, err := data.GetEdbDataMapList(chartInfoId, chartType, calendar, "", "", mappingList, "")
	if err != nil {
		return
	}

	currDay := time.Now()
	currDay = time.Date(currDay.Year(), currDay.Month(), currDay.Day(), 0, 0, 0, 0, time.Local)

	dataMap := make(map[string]float64)
	dateMap := make(map[string]string)
	dateTypeMap := make(map[int]int) // 日期配置key对应的日期类型
	daysAgoMap := make(map[int]int)  // 日期配置key对应的N天前的N值
	showTipsMap := make(map[int]int) // 日期配置key对应点是否显示标注

	for dateIndex, dateConfig := range config.DateConfigList {
		dateTypeMap[dateIndex] = dateConfig.DateType
		daysAgoMap[dateIndex] = dateConfig.Num
		showTipsMap[dateIndex] = dateConfig.ShowTips
		for _, edbInfoMapping := range mappingList {
			// 数据会是正序的
			dataList, ok := edbDataListMap[edbInfoMapping.EdbInfoId]
			if !ok {
				continue
			}

			lenData := len(dataList)
			if lenData <= 0 {
				continue
			}
			// 数据的开始索引
			k := lenData - 1

			// 数据的最晚日期
			dataEndDateStr := dataList[k].DataTime
			dataEndDate, tmpErr := time.ParseInLocation(utils.FormatDate, dataEndDateStr, time.Local)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			// 数据开始日期
			endDateStr := ``
			var endDate time.Time

			var currVal float64
			switch dateConfig.DateType {
			case 1: // 1：最新日期；
				endDateStr = dataEndDateStr
				endDate = dataEndDate
				currVal = dataList[k].Value
			case 2: // 2：N天前(原为指标最新日期的N天前, 现为系统日期的N天)
				tmpEndDate := time.Now().AddDate(0, 0, -dateConfig.Num)
				tmpEndDateStr := tmpEndDate.Format(utils.FormatDate)

				for i := k; i >= 0; i-- {
					tmpDateStr := dataList[i].DataTime
					// 如果正好是这一天，那么就直接break了
					if tmpEndDateStr == tmpDateStr {
						k = i
						endDateStr = tmpDateStr
						endDate = tmpEndDate
						currVal = dataList[i].Value
						break
					}
					tmpDate, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDateStr, time.Local)
					if tmpErr != nil {
						err = tmpErr
						return
					}
					// 如果这期的日期晚于选择的日期，那么继续遍历
					if tmpDate.After(tmpEndDate) {
						continue
					}
					k = i
					endDateStr = tmpDateStr
					endDate = tmpDate
					currVal = dataList[i].Value
					break
				}
			case 3: // 固定日期
				if dateConfig.FixDate == "" {
					errMsg = "固定日期不可为空"
					err = fmt.Errorf("固定日期为空")
					return
				}
				strFixDate := dateConfig.FixDate
				fixDate, e := time.ParseInLocation(utils.FormatDate, strFixDate, time.Local)
				if e != nil {
					errMsg = "固定日期格式有误"
					err = fmt.Errorf("固定日期有误, FixDate: %s", dateConfig.FixDate)
					return
				}

				for i := k; i >= 0; i-- {
					strThisDate := dataList[i].DataTime
					// 如果正好是这一天，那么就直接break了
					if strFixDate == strThisDate {
						k = i
						endDateStr = strThisDate
						endDate = fixDate
						currVal = dataList[i].Value
						break
					}
					// 若固定日期无值, 则取固定日期之前, 能找到的第一个值(同上N天前的逻辑)
					thisDate, e := time.ParseInLocation(utils.FormatDate, strThisDate, time.Local)
					if e != nil {
						err = fmt.Errorf("数据日期格式有误: %s", e.Error())
						return
					}
					if thisDate.After(fixDate) {
						continue
					}
					k = i
					endDateStr = strThisDate
					endDate = thisDate
					currVal = dataList[i].Value
					break
				}
			}

			// 没有找到日期，那么就不处理
			if endDateStr == `` || endDate.IsZero() {
				continue
			}

			// 最早的日期
			earliestDate := endDate.AddDate(0, 0, -config.CalculateValue*moveUnitDays)
			earliestDateStr := earliestDate.Format(utils.FormatDate)

			var percentVal float64 // 百分位计算值
			// 百分位数据区间算法
			if config.PercentType == utils.PercentCalculateTypeRange {
				var minVal, maxVal float64
				var isNotFirst bool // 是否是第一条数据
				for i := k; i >= 0; i-- {
					tmpData := dataList[i]
					if !isNotFirst {
						maxVal = tmpData.Value
						minVal = tmpData.Value
						isNotFirst = true
						continue
					}

					tmpDateStr := dataList[i].DataTime
					// 如果正好是这一天，那么就直接break了
					if earliestDateStr == tmpDateStr {
						break
					}
					tmpDate, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDateStr, time.Local)
					if tmpErr != nil {
						err = tmpErr
						return
					}
					// 如果这期的日期早于选择的日期，那么继续停止遍历
					if tmpDate.Before(earliestDate) {
						continue
					}

					if tmpData.Value > maxVal {
						maxVal = tmpData.Value
					}
					if tmpData.Value < minVal {
						minVal = tmpData.Value
					}
				}

				// 最大值等于最小值，说明计算结果无效
				if maxVal == minVal {
					continue
				}
				// 数据区间百分位=(现值-Min)/(Max-Min)
				tmpV := (currVal - minVal) / (maxVal - minVal) * 100
				percentVal, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
			}

			// 百分位数据个数算法
			// 数据区间第一个和最后一个数据点的时间和数据分别为(T1,S1)(T2,S2); N=T1到T2指标数据个数, n=小于等于S2的数据个数
			// 个数百分位=(n-1)/(N-1)
			if config.PercentType == utils.PercentCalculateTypeNum {
				// T1为earliestDate, T2为endDate, S2为currVal
				var tinyN, bigN int
				lastVal := decimal.NewFromFloat(currVal)
				for i := k; i >= 0; i-- {
					date, e := time.ParseInLocation(utils.FormatDate, dataList[i].DataTime, time.Local)
					if e != nil {
						err = fmt.Errorf("数据日期格式有误: %s", e.Error())
						return
					}
					if !date.Before(earliestDate) && !date.After(endDate) {
						bigN += 1
						dateVal := decimal.NewFromFloat(dataList[i].Value)
						if dateVal.LessThanOrEqual(lastVal) {
							tinyN += 1
						}
					}
				}
				// N=1时说明计算无效
				if bigN == 1 {
					continue
				}
				numerator := decimal.NewFromInt(int64(tinyN - 1))
				denominator := decimal.NewFromInt(int64(bigN - 1))
				percentVal, _ = numerator.Div(denominator).Mul(decimal.NewFromFloat(100)).Round(4).Float64()
			}

			// key的生成（日期配置下标+指标id)
			key := fmt.Sprint(dateIndex, "_", edbInfoMapping.EdbInfoId)
			dataMap[key] = percentVal
			dateMap[key] = endDateStr
		}
	}

	// 返回数据处理
	dataList := make([]SectionScatterSeriesItemResp, 0)
	var xMinVal, xMaxVal, yMinVal, yMaxVal float64
	var isNotFirst bool

	for _, varietyId := range config.VarietyList {
		xEdbInfoId, ok1 := xVarietyEdbMap[varietyId]
		if !ok1 {
			continue
		}
		yEdbInfoId, ok2 := yVarietyEdbMap[varietyId]
		if !ok2 {
			continue
		}
		variety, ok := varietyMap[varietyId]
		if !ok {
			continue
		}

		coordinatePointList := make([]CoordinatePoint, 0)

		for dateIndex, _ := range config.DateConfigList {
			key1 := fmt.Sprint(dateIndex, "_", xEdbInfoId)
			xVal, ok1 := dataMap[key1]
			if !ok1 {
				continue
			}
			key2 := fmt.Sprint(dateIndex, "_", yEdbInfoId)
			yVal, ok2 := dataMap[key2]
			if !ok2 {
				continue
			}
			if !isNotFirst {
				xMinVal = xVal
				xMaxVal = xVal
				yMinVal = yVal
				yMaxVal = yVal
				isNotFirst = true
			} else {
				if xVal < xMinVal {
					xMinVal = xVal
				}
				if xVal > xMaxVal {
					xMaxVal = xVal
				}
				if yVal < yMinVal {
					yMinVal = yVal
				}
				if yVal > yMaxVal {
					yMaxVal = yVal
				}
			}
			coordinatePointList = append(coordinatePointList, CoordinatePoint{
				X:          xVal,
				Y:          yVal,
				XEdbInfoId: xEdbInfoId,
				YEdbInfoId: yEdbInfoId,
				XDate:      dateMap[key1],
				YDate:      dateMap[key2],
				DateType:   dateTypeMap[dateIndex], // 日期类型
				DaysAgo:    daysAgoMap[dateIndex],  // N天前的N值
				ShowTips:   showTipsMap[dateIndex], // 是否显示标注
			})
		}

		dataList = append(dataList, SectionScatterSeriesItemResp{
			Name:                variety.ChartVarietyName,
			NameEn:              variety.ChartVarietyNameEn,
			Color:               "",
			CoordinatePointData: coordinatePointList,
		})
	}

	// 处理颜色
	colorMap := utils.GetColorMap()
	for k, _ := range dataList {
		if c, ok1 := colorMap[k]; ok1 {
			dataList[k].Color = c
		}
	}
	dataResp = ChartInfoResp{
		XName:       xTagInfo.ChartTagName + "百分位",
		XNameEn:     xTagInfo.ChartTagNameEn,
		XUnitName:   "%",
		XUnitNameEn: "%",
		YName:       yTagInfo.ChartTagName + "百分位",
		YNameEn:     yTagInfo.ChartTagNameEn,
		YUnitName:   "%",
		YUnitNameEn: "%",
		XMinValue:   fmt.Sprint(xMinVal),
		XMaxValue:   fmt.Sprint(xMaxVal),
		YMinValue:   fmt.Sprint(yMinVal),
		YMaxValue:   fmt.Sprint(yMaxVal),
		DataList:    dataList,
	}

	// 去除返回指标中的数据信息，避免没必要的数据传输
	for k, _ := range edbList {
		edbList[k].DataList = nil
	}

	return
}

// CheckIsEnChart
// @Description: 检测是否展示英文名称
// @author: Roc
// @datetime 2023-11-28 20:54:41
// @param chartNameEn string
// @param dataResp ChartInfoResp
// @return bool
func CheckIsEnChart(chartNameEn string, dataResp ChartInfoResp) bool {
	if chartNameEn == "" {
		return false
	}

	if dataResp.XNameEn == `` {
		return false
	}
	if dataResp.YNameEn == `` {
		return false
	}
	for _, v := range dataResp.DataList {
		if v.NameEn == `` {
			return false
		}
	}
	return true
}

// GetXYEdbIdList
// @Description: 根据标签id和品种获取指标列表信息
// @author: Roc
// @datetime 2023-11-27 14:31:23
// @param tagX int
// @param tagY int
// @param varietyList []int
// @return xVarietyEdbMap map[int]int
// @return yVarietyEdbMap map[int]int
// @return edbInfoIdList []int
// @return errMsg string
// @return err error
func GetXYEdbIdList(tagX, tagY int, varietyList []int) (xVarietyEdbMap, yVarietyEdbMap map[int]int, edbInfoIdList []int, err error) {
	edbInfoIdList = make([]int, 0)
	xVarietyEdbMap = make(map[int]int)
	yVarietyEdbMap = make(map[int]int)
	xList, err := cross_varietyModel.GetChartTagVarietyListByTagAndVariety(tagX, varietyList)
	if err != nil {
		err = errors.New("获取X轴的品种指标配置信息失败，Err:" + err.Error())
		return
	}
	yList, err := cross_varietyModel.GetChartTagVarietyListByTagAndVariety(tagY, varietyList)
	if err != nil {
		err = errors.New("获取Y轴的品种指标配置信息失败，Err:" + err.Error())
		return
	}

	baseVarietyIdMap := make(map[int]int)
	for _, v := range xList {
		baseVarietyIdMap[v.ChartVarietyId] = v.ChartVarietyId
	}

	// 两个标签里面的品种并集
	needVarietyIdMap := make(map[int]int)
	for _, v := range yList {
		if val, ok := baseVarietyIdMap[v.ChartVarietyId]; ok {
			// 如果在 map2 中存在相同的键，则将键和值添加到结果中
			needVarietyIdMap[v.ChartVarietyId] = val
		}
	}

	for _, v := range xList {
		if _, ok := needVarietyIdMap[v.ChartVarietyId]; ok {
			xVarietyEdbMap[v.ChartVarietyId] = v.EdbInfoId
			edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
		}
	}
	for _, v := range yList {
		if _, ok := needVarietyIdMap[v.ChartVarietyId]; ok {
			yVarietyEdbMap[v.ChartVarietyId] = v.EdbInfoId
			edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
		}
	}

	return
}

// AddChartInfo
// @Description: AddChartInfo
// @author: Roc
// @datetime 2023-11-24 15:58:14
// @param req request.AddChartReq
// @param sysUser *system.Admin
// @return chartInfo *data_manage.ChartInfo
// @return lang string
// @return err error
// @return errMsg string
// @return isSendEmail bool
func AddChartInfo(req request.AddChartReq, sysUser *system.Admin, lang string) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
	isSendEmail = true

	source := utils.CHART_SOURCE_CROSS_HEDGING

	req.ChartName = strings.Trim(req.ChartName, " ")
	if req.ChartName == "" {
		errMsg = "请填写图表名称！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.TagX <= 0 {
		errMsg = "请选择X轴坐标的标签！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.TagY <= 0 {
		errMsg = "请选择Y轴坐标的标签！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.CalculateValue <= 0 {
		errMsg = "请设置时间长度！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.CalculateUnit == `` {
		errMsg = "请设置时间频度！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.PercentType != utils.PercentCalculateTypeRange && req.PercentType != utils.PercentCalculateTypeNum {
		errMsg = "请选择百分位"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	// 品种配置
	if len(req.VarietyList) < 0 {
		errMsg = "请选择品种！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	// 日期配置
	dateConfigList := len(req.DateConfigList)
	if dateConfigList < 0 {
		errMsg = "请选择日期！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if dateConfigList > 5 {
		errMsg = "日期数量已达上限！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}

	// 基础配置转string
	extraConfigByte, err := json.Marshal(req)
	if err != nil {
		return
	}

	chartClassify, err := data_manage.GetCrossVarietyChartClassifyBySysUserId(sysUser.AdminId)
	if err != nil {
		if err.Error() != utils.ErrNoRow() {
			errMsg = "获取分类信息失败"
			err = errors.New("获取分类信息失败，Err:" + err.Error())
			return
		}

		// 分类没有，需要创建新的分类，那么err置空
		err = nil

		_, err, errMsg, isSendEmail = data.AddChartClassify(sysUser.RealName, 0, 1, source, lang, sysUser)
		if err != nil {
			return
		}
	}

	// 数据校验（品种、标签、指标）
	{
		// 标签m
		var xTagInfo, yTagInfo *cross_varietyModel.ChartTag
		{
			tagList, tmpErr := cross_varietyModel.GetTagListByIdList([]int{req.TagX, req.TagY})
			if tmpErr != nil {
				err = tmpErr
				return
			}
			for _, v := range tagList {
				if v.ChartTagId == req.TagX {
					xTagInfo = v
				} else if v.ChartTagId == req.TagY {
					yTagInfo = v
				}
			}
		}
		if xTagInfo == nil {
			errMsg = "找不到对应的X轴标签"
			err = errors.New(errMsg)
			return
		}
		if yTagInfo == nil {
			errMsg = "找不到对应的Y轴标签"
			err = errors.New(errMsg)
			return
		}

		_, _, edbInfoIdList, tmpErr := GetXYEdbIdList(req.TagX, req.TagY, req.VarietyList)
		if tmpErr != nil {
			err = errors.New("获取标签配置的品种失败，ERR：" + tmpErr.Error())
			return
		}

		if len(edbInfoIdList) <= 0 {
			errMsg = "品种未配置指标"
			err = errors.New(errMsg)
			isSendEmail = false
			return
		}
		mappingList, tmpErr := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
		if tmpErr != nil {
			errMsg = "获取指标信息失败"
			err = errors.New("获取指标信息失败，ERR：" + tmpErr.Error())
			return
		}

		if len(mappingList) <= 0 {
			errMsg = "关联指标为空"
			err = errors.New(errMsg)
			isSendEmail = false
			return
		}
	}

	var chartInfoId int
	// 判断图表是否存在
	//var condition string
	//var pars []interface{}
	//condition += " AND chart_name=? AND source = ? "
	//pars = append(pars, req.ChartName, source)
	//count, err := data_manage.GetChartInfoCountByCondition(condition, pars)
	//if err != nil {
	//	errMsg = "判断图表名称是否存在失败"
	//	err = errors.New("判断图表名称是否存在失败,Err:" + err.Error())
	//	return
	//}

	//if count > 0 {
	//	errMsg = "图表已存在，请重新填写"
	//	err = errors.New(errMsg)
	//	isSendEmail = false
	//	return
	//}

	chartInfo = new(data_manage.ChartInfo)
	chartInfo.ChartName = req.ChartName
	chartInfo.ChartNameEn = req.ChartNameEn
	//chartInfo.EdbInfoIds = edbInfoIdStr
	//chartInfo.ChartClassifyId = req.ChartClassifyId
	chartInfo.SysUserId = sysUser.AdminId
	chartInfo.SysUserRealName = sysUser.RealName
	chartInfo.ChartImage = req.ChartImage
	chartInfo.CreateTime = time.Now()
	chartInfo.ModifyTime = time.Now()
	chartInfo.IsSetName = 0
	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
	chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
	chartInfo.ChartType = 9 // 相关性图
	chartInfo.Calendar = "公历"
	chartInfo.DateType = 6
	//chartInfo.StartDate = req.StartDate
	//chartInfo.EndDate = req.EndDate
	//chartInfo.SeasonStartDate = req.StartDate
	//chartInfo.SeasonEndDate = req.EndDate
	chartInfo.LeftMin = req.LeftMin
	chartInfo.LeftMax = req.LeftMax
	chartInfo.XMin = req.XMin
	chartInfo.XMax = req.XMax
	//chartInfo.RightMin = req.RightMin
	//chartInfo.RightMax = req.RightMax
	//chartInfo.Disabled = disableVal
	chartInfo.Source = source
	chartInfo.ExtraConfig = string(extraConfigByte)
	chartInfo.ChartImage = req.ChartImage
	chartInfo.ChartThemeId = req.ChartThemeId
	chartInfo.SourcesFrom = req.SourcesFrom
	chartInfo.Instructions = req.Instructions
	chartInfo.MarkersLines = req.MarkersLines
	chartInfo.MarkersAreas = req.MarkersAreas

	// 图表品种
	chartVarietyMappingList := make([]*cross_varietyModel.ChartVarietyMapping, 0)
	for _, varietyId := range req.VarietyList {
		chartVarietyMappingList = append(chartVarietyMappingList, &cross_varietyModel.ChartVarietyMapping{
			Id:             0,
			ChartInfoId:    0,
			ChartVarietyId: varietyId,
			ModifyTime:     time.Now(),
			CreateTime:     time.Now(),
		})
	}

	// 图表配置
	chartInfoCrossVariety := &cross_varietyModel.ChartInfoCrossVariety{
		Id:             0,
		ChartInfoId:    0,
		ChartXTagId:    req.TagX,
		ChartYTagId:    req.TagY,
		CalculateValue: req.CalculateValue,
		CalculateUnit:  req.CalculateUnit,
		ModifyTime:     time.Now(),
		CreateTime:     time.Now(),
	}

	// 新增图表和指标mapping
	chartInfoId, e := cross_varietyModel.CreateChart(chartInfo, chartClassify, chartVarietyMappingList, chartInfoCrossVariety)
	if e != nil {
		errMsg = "操作失败"
		err = errors.New("新增相关性图表失败, Err: " + e.Error())
		return
	}

	//添加es数据
	go data.EsAddOrEditChartInfo(chartInfoId)

	return
}

// EditChartInfo
// @Description: 编辑图表
// @author: Roc
// @datetime 2023-11-24 15:58:31
// @param req request.EditChartReq
// @param sysUser *system.Admin
// @return chartItem *data_manage.ChartInfo
// @return err error
// @return errMsg string
// @return isSendEmail bool
func EditChartInfo(req request.EditChartReq, sysUser *system.Admin, lang string) (chartItem *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
	isSendEmail = true

	chartItem, err = data_manage.GetChartInfoById(req.ChartInfoId)
	if err != nil {
		if err.Error() == utils.ErrNoRow() {
			errMsg = "图表已被删除，请刷新页面"
			err = errors.New(errMsg)
			isSendEmail = false
			return
		}
		errMsg = "获取图表信息失败"
		err = errors.New("获取图表信息失败,Err:" + err.Error())
		return
	}

	if chartItem.Source != utils.CHART_SOURCE_CROSS_HEDGING {
		errMsg = "该图不是跨品种分析图表！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}

	req.ChartName = strings.Trim(req.ChartName, " ")
	if req.ChartName == "" {
		errMsg = "请填写图表名称！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.TagX <= 0 {
		errMsg = "请选择X轴坐标的标签！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.TagY <= 0 {
		errMsg = "请选择Y轴坐标的标签！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.CalculateValue <= 0 {
		errMsg = "请设置时间长度！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.CalculateUnit == `` {
		errMsg = "请设置时间频度！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.PercentType != utils.PercentCalculateTypeRange && req.PercentType != utils.PercentCalculateTypeNum {
		errMsg = "请选择百分位"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	// 品种配置
	if len(req.VarietyList) < 0 {
		errMsg = "请选择品种！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	// 日期配置
	dateConfigList := len(req.DateConfigList)
	if dateConfigList < 0 {
		errMsg = "请选择日期！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if dateConfigList > 5 {
		errMsg = "日期数量已达上限！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	// 基础配置转string
	extraConfigByte, err := json.Marshal(req)
	if err != nil {
		return
	}

	// 图表操作权限
	ok := data.CheckOpChartPermission(sysUser, chartItem.SysUserId, true)
	if !ok {
		errMsg = "没有该图表的操作权限"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	
	// 数据校验（品种、标签、指标）
	{
		// 标签m
		var xTagInfo, yTagInfo *cross_varietyModel.ChartTag
		{
			tagList, tmpErr := cross_varietyModel.GetTagListByIdList([]int{req.TagX, req.TagY})
			if tmpErr != nil {
				err = tmpErr
				return
			}
			for _, v := range tagList {
				if v.ChartTagId == req.TagX {
					xTagInfo = v
				} else if v.ChartTagId == req.TagY {
					yTagInfo = v
				}
			}
		}
		if xTagInfo == nil {
			errMsg = "找不到对应的X轴标签"
			err = errors.New(errMsg)
			return
		}
		if yTagInfo == nil {
			errMsg = "找不到对应的Y轴标签"
			err = errors.New(errMsg)
			return
		}

		_, _, edbInfoIdList, tmpErr := GetXYEdbIdList(req.TagX, req.TagY, req.VarietyList)
		if tmpErr != nil {
			err = errors.New("获取标签配置的品种失败，ERR：" + tmpErr.Error())
			return
		}

		if len(edbInfoIdList) <= 0 {
			errMsg = "品种未配置指标"
			err = errors.New(errMsg)
			isSendEmail = false
			return
		}
		mappingList, tmpErr := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
		if tmpErr != nil {
			errMsg = "获取指标信息失败"
			err = errors.New("获取指标信息失败，ERR：" + tmpErr.Error())
			return
		}

		if len(mappingList) <= 0 {
			errMsg = "关联指标为空"
			err = errors.New(errMsg)
			isSendEmail = false
			return
		}
	}

	//判断图表是否存在
	//var condition string
	//var pars []interface{}
	//condition += " AND chart_info_id <> ? "
	//pars = append(pars, req.ChartInfoId)
	//condition += " AND chart_name=? AND source = ? "
	//pars = append(pars, req.ChartName, chartItem.Source)
	//count, err := data_manage.GetChartInfoCountByCondition(condition, pars)
	//if err != nil {
	//	errMsg = "判断图表名称是否存在失败"
	//	err = errors.New("判断图表名称是否存在失败,Err:" + err.Error())
	//	return
	//}
	//if count > 0 {
	//	errMsg = "图表已存在，请重新填写"
	//	err = errors.New(errMsg)
	//	isSendEmail = false
	//	return
	//}

	switch lang {
	case utils.EnLangVersion:
		chartItem.ChartNameEn = req.ChartName
	default:
		chartItem.ChartName = req.ChartName
	}
	chartItem.ExtraConfig = string(extraConfigByte)
	chartItem.ModifyTime = time.Now()
	chartItem.LeftMin = req.LeftMin
	chartItem.LeftMax = req.LeftMax
	chartItem.XMin = req.XMin
	chartItem.XMax = req.XMax
	chartUpdateCols := []string{"ChartName", "ChartNameEn", "ExtraConfig", "ModifyTime", "LeftMin", "LeftMax", "XMin", "XMax"}

	// 跨品种分析配置
	chartInfoCrossVariety, err := cross_varietyModel.GetChartInfoCrossVarietyByChartInfoId(chartItem.ChartInfoId)
	if err != nil {
		return
	}
	chartInfoCrossVariety.ChartXTagId = req.TagX
	chartInfoCrossVariety.ChartYTagId = req.TagY
	chartInfoCrossVariety.CalculateValue = req.CalculateValue
	chartInfoCrossVariety.CalculateUnit = req.CalculateUnit
	chartInfoCrossVariety.ModifyTime = time.Now()
	chartInfoCrossVarietyUpdateCols := []string{"ChartXTagId", "ChartYTagId", "CalculateValue", "CalculateUnit", "ModifyTime"}

	// 图表品种
	chartVarietyMappingList := make([]*cross_varietyModel.ChartVarietyMapping, 0)
	for _, varietyId := range req.VarietyList {
		chartVarietyMappingList = append(chartVarietyMappingList, &cross_varietyModel.ChartVarietyMapping{
			Id:             0,
			ChartInfoId:    0,
			ChartVarietyId: varietyId,
			ModifyTime:     time.Now(),
			CreateTime:     time.Now(),
		})
	}

	err = cross_varietyModel.EditChart(chartItem, chartVarietyMappingList, chartInfoCrossVariety,
		chartUpdateCols, chartInfoCrossVarietyUpdateCols)
	if err != nil {
		errMsg = "保存失败"
		err = errors.New("保存失败,Err:" + err.Error())
		return
	}

	resp := new(data_manage.AddChartInfoResp)
	resp.ChartInfoId = chartItem.ChartInfoId
	resp.UniqueCode = chartItem.UniqueCode
	//resp.ChartType = req.ChartType

	//添加es数据
	go data.EsAddOrEditChartInfo(chartItem.ChartInfoId)
	//修改my eta es数据
	go data.EsAddOrEditMyChartInfoByChartInfoId(chartItem.ChartInfoId)

	return
}

// ModifyChartEdbMapping
// @Description: 修改图表的关系表
// @author: Roc
// @datetime 2023-12-11 17:32:31
// @param tagId int
func ModifyChartEdbMapping(tagId int) {
	errMsgList := make([]string, 0)
	var err error
	defer func() {
		if err != nil {
			errMsgList = append(errMsgList, err.Error())
		}
		if len(errMsgList) > 0 {
			utils.FileLog.Error("修改跨品种分析的图表关联指标失败：\n" + strings.Join(errMsgList, "\n"))
		}
	}()
	// 找出标签关联的图表
	list, err := cross_varietyModel.GeChartInfoCrossVarietyListByTagId(tagId)
	if err != nil {
		return
	}

	chartInfoIdList := make([]int, 0)

	tagIdList := make([]int, 0)
	tagIdMap := make(map[int]int, 0)
	for _, v := range list {
		chartInfoIdList = append(chartInfoIdList, v.ChartInfoId)

		if _, ok := tagIdMap[v.ChartXTagId]; !ok {
			tagIdList = append(tagIdList, v.ChartXTagId)
			tagIdMap[v.ChartXTagId] = 1
		}

		if _, ok := tagIdMap[v.ChartYTagId]; !ok {
			tagIdList = append(tagIdList, v.ChartYTagId)
			tagIdMap[v.ChartYTagId] = 1
		}
	}

	// 找出所有标签关联的品种和对应的指标
	chartTagVarietyList, err := cross_varietyModel.GetChartTagVarietyListByTagIdList(tagIdList)
	if err != nil {
		return
	}

	// 所有指标id
	allEdbInfoIdList := make([]int, 0)

	// 标签关联品种的指标列表
	tagVarietyEdbMap := make(map[string][]int)
	for _, v := range chartTagVarietyList {
		if v.EdbInfoId <= 0 {
			continue
		}
		key := fmt.Sprint(v.ChartTagId, "_", v.ChartVarietyId)
		tagVarietyEdbList, ok := tagVarietyEdbMap[key]
		if !ok {
			tagVarietyEdbList = make([]int, 0)
		}
		tagVarietyEdbMap[key] = append(tagVarietyEdbList, v.EdbInfoId)
		allEdbInfoIdList = append(allEdbInfoIdList, v.EdbInfoId)
	}

	edbInfoMap := make(map[int]*data_manage.EdbInfo)
	// 获取指标列表
	if len(allEdbInfoIdList) > 0 {
		allEdbInfoList, tmpErr := data_manage.GetEdbInfoByIdList(allEdbInfoIdList)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		for _, v := range allEdbInfoList {
			edbInfoMap[v.EdbInfoId] = v
		}
	}

	// 图表关联的品种列表
	chartVarietyMappingList, err := cross_varietyModel.GetChartVarietyMappingListByChartInfoIdList(chartInfoIdList)
	if err != nil {
		return
	}

	// 图表关联的品种map
	chartVarietyMappingListMap := make(map[int][]int)
	for _, v := range chartVarietyMappingList {
		if v.ChartInfoId <= 0 {
			continue
		}
		varietyMappingList, ok := chartVarietyMappingListMap[v.ChartInfoId]
		if !ok {
			varietyMappingList = make([]int, 0)
		}
		chartVarietyMappingListMap[v.ChartInfoId] = append(varietyMappingList, v.ChartVarietyId)
	}

	// 处理图表
	for _, v := range list {
		// 获取关联的指标id
		edbInfoList := make([]*data_manage.EdbInfo, 0)
		edbInfoIdMap := make(map[int]int, 0)

		// 找出图表关联的品种
		varietyIdList, ok := chartVarietyMappingListMap[v.ChartInfoId]
		if ok {
			// 获取品种与标签的id
			for _, varietyId := range varietyIdList {
				// 先处理x轴
				key := fmt.Sprint(v.ChartXTagId, "_", varietyId)
				if tmpEdbInfoIdList, ok2 := tagVarietyEdbMap[key]; ok2 {
					for _, edbInfoId := range tmpEdbInfoIdList {
						if _, ok3 := edbInfoIdMap[edbInfoId]; !ok3 {
							if edbInfo, ok4 := edbInfoMap[edbInfoId]; ok4 {
								edbInfoList = append(edbInfoList, edbInfo)
							}
							edbInfoIdMap[edbInfoId] = edbInfoId
						}
					}
				}

				// 处理y轴
				key = fmt.Sprint(v.ChartYTagId, "_", varietyId)
				if tmpEdbInfoIdList, ok2 := tagVarietyEdbMap[key]; ok2 {
					for _, edbInfoId := range tmpEdbInfoIdList {
						if _, ok3 := edbInfoIdMap[edbInfoId]; !ok3 {
							if edbInfo, ok4 := edbInfoMap[edbInfoId]; ok4 {
								edbInfoList = append(edbInfoList, edbInfo)
							}
							edbInfoIdMap[edbInfoId] = edbInfoId
						}
					}
				}
			}
		}

		tmpErr := data_manage.ModifyChartEdbMapping(v.ChartInfoId, edbInfoList)
		if tmpErr != nil {
			errMsgList = append(errMsgList, fmt.Sprint("修改", v.ChartInfoId, "失败，err:", tmpErr))
		}
	}

	return
}
