package line_equation

import (
	"errors"
	"github.com/shopspring/decimal"
	"hongze/hongze_yb/models/request"
	chartEdbMappingModel "hongze/hongze_yb/models/tables/chart_edb_mapping"
	edbDataModel "hongze/hongze_yb/models/tables/edb_data"
	"hongze/hongze_yb/services/chart"
	"hongze/hongze_yb/utils"
	"math"
	"time"
)

type LineEquationResp struct {
	AData  LineEquationDataResp
	BData  LineEquationDataResp
	R2Data LineEquationDataResp
}

type LineEquationDataResp struct {
	MaxData             float64
	MinData             float64
	LatestDate          time.Time `description:"真实数据的最后日期"`
	EdbInfoCategoryType int
	ChartColor          string
	ChartStyle          string
	PredictChartColor   string
	ChartType           int
	ChartWidth          int
	EdbName             string
	EdbNameEn           string
	Unit                string
	UnitEn              string
	IsAxis              int
	DataList            []edbDataModel.EdbDataList
}

// GetChartEdbData 获取图表数据
func GetChartEdbData(chartInfoId int, lineChartInfoConfig request.LineChartInfoReq, getAData, getBData, getR2Data bool) (edbList []*chartEdbMappingModel.ChartEdbInfoMappingList, dataResp LineEquationResp, sourceArr []string, err error, errMsg string) {
	// 获取基本信息
	mappingList, startDate, endDate, err, errMsg := getConfigData(lineChartInfoConfig)
	if err != nil {
		return
	}
	xEdbInfoIdList := lineChartInfoConfig.XEdbInfoIdList
	yEdbInfoIdList := lineChartInfoConfig.YEdbInfoIdList

	aLineEquationDataResp := LineEquationDataResp{
		DataList:          make([]edbDataModel.EdbDataList, 0),
		MaxData:           0,
		MinData:           0,
		ChartColor:        "#00f",
		ChartStyle:        `spline`,
		PredictChartColor: `#00f`,
		ChartType:         0,
		ChartWidth:        3,
		EdbName:           `弹性系数`,
		EdbNameEn:         `elastic coefficient`,
		IsAxis:            1,
	}
	bLineEquationDataResp := LineEquationDataResp{
		DataList:          make([]edbDataModel.EdbDataList, 0),
		MaxData:           0,
		MinData:           0,
		ChartColor:        "#00f",
		ChartStyle:        `spline`,
		PredictChartColor: `#00f`,
		ChartType:         0,
		ChartWidth:        3,
		EdbName:           `截距`,
		EdbNameEn:         `intercept`,
		IsAxis:            1,
	}
	r2LineEquationDataResp := LineEquationDataResp{
		DataList:          make([]edbDataModel.EdbDataList, 0),
		MaxData:           0,
		MinData:           0,
		ChartColor:        "#00f",
		ChartStyle:        `spline`,
		PredictChartColor: `#00f`,
		ChartType:         0,
		ChartWidth:        3,
		EdbName:           `相关系数`,
		EdbNameEn:         `coefficient of association`,
		IsAxis:            1,
	}
	edbList = make([]*chartEdbMappingModel.ChartEdbInfoMappingList, 0)
	dataResp = LineEquationResp{
		AData:  aLineEquationDataResp,
		BData:  bLineEquationDataResp,
		R2Data: r2LineEquationDataResp,
	}

	var baseEdbInfo *chartEdbMappingModel.ChartEdbInfoMapping
	// 获取确定以哪个指标的日期作为基准日期
	{
		var xEdbInfo, yEdbInfo *chartEdbMappingModel.ChartEdbInfoMapping
		for _, v := range mappingList {
			if v.EdbInfoId == xEdbInfoIdList[0] {
				xEdbInfo = v
			} else if v.EdbInfoId == yEdbInfoIdList[0] {
				yEdbInfo = v
			}
		}
		if xEdbInfo == nil {
			errMsg = `X轴第一个指标异常`
			err = errors.New(errMsg)
			return
		}
		if yEdbInfo == nil {
			errMsg = `Y轴第一个指标异常`
			err = errors.New(errMsg)
			return
		}

		// 时间截面规则：按照什么频率来取不同的截面的问题。原则是：按照X轴和Y轴所选择的第一个指标（X和Y分别有一个第一个指标），两个指标中，以低频的那个作为基准。如果X轴和Y轴是同频的，那以Y轴第一个指标的为准。
		frequencyIntMap := map[string]int{
			"日度": 1,
			"周度": 2,
			"旬度": 3,
			"月度": 4,
			"季度": 5,
			"年度": 6,
		}
		// 如果x是高频 或者 x与y是同频的，那么就是Y轴的第一个指标为主
		if frequencyIntMap[xEdbInfo.Frequency] <= frequencyIntMap[yEdbInfo.Frequency] {
			baseEdbInfo = yEdbInfo
		} else {
			// 否则是X轴的第一个指标是低频
			baseEdbInfo = xEdbInfo
		}
	}

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

	// 获取所有的日期
	dateList := make([]string, 0)
	for _, v := range edbDataListMap[baseEdbInfo.EdbInfoId] {
		dateList = append(dateList, v.DataTime)
	}

	// 数据整理
	// [日期][A指标id:值，B指标id:值]
	dateEdbMap, err := handleData(baseEdbInfo.EdbInfoId, edbDataListMap)
	if err != nil {
		return
	}

	lenX := len(xEdbInfoIdList)

	var isNotAFirst, isNotBFirst, isNotR2First bool
	for i, date := range dateList {
		coordinateData := make([]utils.Coordinate, 0)
		for k := 0; k < lenX; k++ {
			xVal, ok1 := dateEdbMap[date][xEdbInfoIdList[k]]
			yVal, ok2 := dateEdbMap[date][yEdbInfoIdList[k]]
			if !ok1 || !ok2 {
				continue
			}
			tmpCoordinate1 := utils.Coordinate{
				X: xVal,
				Y: yVal,
			}
			coordinateData = append(coordinateData, tmpCoordinate1)
		}
		dataTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
		timestamp := dataTime.UnixNano() / 1e6
		// 只有存在两个坐标点的时候,才能去计算线性方程和R平方
		if len(coordinateData) >= 2 {
			a, b := utils.GetLinearResult(coordinateData)
			if !math.IsNaN(a) && !math.IsNaN(b) && !math.IsInf(a, 0) && !math.IsInf(b, 0) {
				if getAData {
					a, _ = decimal.NewFromFloat(a).Round(4).Float64()
					dataResp.AData.DataList = append(dataResp.AData.DataList, edbDataModel.EdbDataList{
						EdbDataId:     i,
						EdbInfoId:     0,
						DataTime:      date,
						DataTimestamp: timestamp,
						Value:         a,
					})
					if !isNotAFirst {
						dataResp.AData.MinData = a
						dataResp.AData.MaxData = a
						isNotAFirst = true
					}
					if dataResp.AData.MinData > a {
						dataResp.AData.MinData = a
					}
					if dataResp.AData.MaxData < a {
						dataResp.AData.MaxData = a
					}
				}
				if getBData {
					b, _ = decimal.NewFromFloat(b).Round(4).Float64()
					dataResp.BData.DataList = append(dataResp.BData.DataList, edbDataModel.EdbDataList{
						EdbDataId:     i,
						EdbInfoId:     0,
						DataTime:      date,
						DataTimestamp: timestamp,
						Value:         b,
					})
					if !isNotBFirst {
						dataResp.BData.MinData = b
						dataResp.BData.MaxData = b
						isNotBFirst = true
					}
					if dataResp.BData.MinData > b {
						dataResp.BData.MinData = b
					}
					if dataResp.BData.MaxData < b {
						dataResp.BData.MaxData = b
					}
				}
			}

			// 计算R平方
			if getR2Data {
				tmpVal := utils.CalculationDecisive(coordinateData)
				if math.IsNaN(tmpVal) || math.IsInf(tmpVal, 0) {
					continue
				}

				tmpVal, _ = decimal.NewFromFloat(tmpVal).Round(4).Float64()
				dataResp.R2Data.DataList = append(dataResp.R2Data.DataList, edbDataModel.EdbDataList{
					EdbDataId:     i,
					EdbInfoId:     0,
					DataTime:      date,
					DataTimestamp: timestamp,
					Value:         tmpVal,
				})
				if !isNotR2First {
					dataResp.R2Data.MinData = tmpVal
					dataResp.R2Data.MaxData = tmpVal
					isNotR2First = true
				}
				if dataResp.R2Data.MinData > tmpVal {
					dataResp.R2Data.MinData = tmpVal
				}
				if dataResp.R2Data.MaxData < tmpVal {
					dataResp.R2Data.MaxData = tmpVal
				}
			}
		}
	}
	dataResp.AData.LatestDate = baseEdbInfo.LatestDate
	dataResp.AData.EdbInfoCategoryType = baseEdbInfo.EdbInfoCategoryType
	dataResp.BData.LatestDate = baseEdbInfo.LatestDate
	dataResp.BData.EdbInfoCategoryType = baseEdbInfo.EdbInfoCategoryType
	dataResp.R2Data.LatestDate = baseEdbInfo.LatestDate
	dataResp.R2Data.EdbInfoCategoryType = baseEdbInfo.EdbInfoCategoryType

	return
}

// getConfigData 获取配置数据
func getConfigData(lineChartInfoConfig request.LineChartInfoReq) (mappingList []*chartEdbMappingModel.ChartEdbInfoMapping, startDate, endDate string, err error, errMsg string) {
	dateType := lineChartInfoConfig.DateType
	switch dateType {
	case 1:
		startDate = "2000-01-01"
	case 2:
		startDate = "2010-01-01"
	case 3:
		startDate = "2015-01-01"
	case 4:
		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
		startDate = "2021-01-01"
	case 5:
		startDate = lineChartInfoConfig.StartDate + "-01"
		endDate = lineChartInfoConfig.EndDate + "-01"
	case 6:
		startDate = lineChartInfoConfig.StartDate + "-01"
	case 7:
		startDate = "2018-01-01"
	case 8:
		startDate = "2019-01-01"
	case 9:
		startDate = "2020-01-01"
	case 11:
		startDate = "2022-01-01"
	}

	//指标数据
	edbInfoIdList := make([]int, 0)
	{
		edbInfoIdMap := make(map[int]int, 0)
		for _, edbInfoId := range lineChartInfoConfig.XEdbInfoIdList {
			edbInfoIdMap[edbInfoId] = edbInfoId
		}
		for _, edbInfoId := range lineChartInfoConfig.YEdbInfoIdList {
			edbInfoIdMap[edbInfoId] = edbInfoId
		}
		for _, edbInfoId := range edbInfoIdMap {
			edbInfoIdList = append(edbInfoIdList, edbInfoId)
		}
	}
	mappingList, err = chartEdbMappingModel.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
	if err != nil {
		errMsg = `获取失败`
		err = errors.New("获取图表，指标信息失败，Err:" + err.Error())
		return
	}

	return
}

// handleData 数据处理
func handleData(baseEdbInfoId int, edbDataListMap map[int][]*edbDataModel.EdbDataList) (dateEdbMap map[string]map[int]float64, err error) {
	dateEdbMap = make(map[string]map[int]float64) // [日期][A指标id:值，B指标id:值]
	for edbInfoId, edbDataList := range edbDataListMap {
		if edbInfoId != baseEdbInfoId {
			// 用上期的数据补充当期的数据处理
			handleDataMap := make(map[string]float64)
			err = chart.HandleDataByPreviousData(edbDataList, handleDataMap)
			if err != nil {
				return
			}

			for date, val := range handleDataMap {
				item, ok := dateEdbMap[date]
				if ok {
					item[edbInfoId] = val
				} else {
					item = map[int]float64{
						edbInfoId: val,
					}
				}
				dateEdbMap[date] = item
			}
		} else {
			for _, edbData := range edbDataList {
				item, ok := dateEdbMap[edbData.DataTime]
				if ok {
					item[edbInfoId] = edbData.Value
				} else {
					item = map[int]float64{
						edbInfoId: edbData.Value,
					}
				}
				dateEdbMap[edbData.DataTime] = item
			}
		}
	}

	return
}
