package cross_variety

import (
	"errors"
	"fmt"
	"github.com/shopspring/decimal"
	"hongze/hongze_yb/models/request"
	chartEdbMappingModel "hongze/hongze_yb/models/tables/chart_edb_mapping"
	chart_tagModel "hongze/hongze_yb/models/tables/chart_tag"
	chart_tag_varietyModel "hongze/hongze_yb/models/tables/chart_tag_variety"
	chart_varietyModel "hongze/hongze_yb/models/tables/chart_variety"
	"hongze/hongze_yb/services/chart"
	"hongze/hongze_yb/utils"
	"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:"趋势线的前后坐标点"`
}

// CoordinatePoint 坐标点
type CoordinatePoint struct {
	X          float64
	Y          float64
	XEdbInfoId int
	YEdbInfoId int
	XDate      string
	YDate      string
}

// 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 []*chartEdbMappingModel.ChartEdbInfoMappingList, dataResp ChartInfoResp, sourceArr []string, 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]*chart_varietyModel.ChartVariety)
	{
		varietyList, tmpErr := chart_varietyModel.GetVarietyListByIdList(config.VarietyList)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		for _, v := range varietyList {
			varietyMap[v.ChartVarietyID] = v
		}
	}

	// 标签m
	var xTagInfo, yTagInfo *chart_tagModel.ChartTag
	{
		tagList, tmpErr := chart_tagModel.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 := chartEdbMappingModel.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
	if err != nil {
		errMsg = "获取指标信息失败"
		err = errors.New("获取指标信息失败,ERR:" + err.Error())
		return
	}

	// 指标对应的所有数据
	chartType := 1 //1:普通图,2:季节性图
	calendar := "公历"
	edbDataListMap, edbList, sourceArr, err := chart.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)
	for dateIndex, dateConfig := range config.DateConfigList {
		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天前
				tmpEndDate := currDay.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
				}
			}

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

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

			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
			tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()

			// key的生成(日期配置下标+指标id)
			key := fmt.Sprint(dateIndex, "_", edbInfoMapping.EdbInfoId)
			dataMap[key] = tmpV
			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],
			})
		}

		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
}

// 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 := chart_tag_varietyModel.GetChartTagVarietyListByTagAndVariety(tagX, varietyList)
	if err != nil {
		err = errors.New("获取X轴的品种指标配置信息失败,Err:" + err.Error())
		return
	}
	yList, err := chart_tag_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[int(v.ChartVarietyID)] = int(v.ChartVarietyID)
	}

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

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

	return
}