package models

import (
	"errors"
	"eta/eta_index_lib/utils"
	"github.com/shopspring/decimal"
	"math"
	"time"
)

// HandleDataByLinearRegression 插值法补充数据(线性方程式)
func HandleDataByLinearRegression(edbInfoDataList []*EdbInfoSearchData, handleDataMap map[string]float64) (newList []*EdbInfoSearchData, err error) {
	if len(edbInfoDataList) < 2 {
		return
	}

	var startEdbInfoData *EdbInfoSearchData
	for _, v := range edbInfoDataList {
		handleDataMap[v.DataTime] = v.Value

		// 第一个数据就给过滤了,给后面的试用
		if startEdbInfoData == nil {
			startEdbInfoData = v
			newList = append(newList, &EdbInfoSearchData{
				EdbDataId: v.EdbDataId,
				DataTime:  v.DataTime,
				Value:     v.Value,
			})
			continue
		}

		// 获取两条数据之间相差的天数
		startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
		currDataTime, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
		betweenHour := int(currDataTime.Sub(startDataTime).Hours())
		betweenDay := betweenHour / 24

		// 如果相差一天,那么过滤
		if betweenDay <= 1 {
			startEdbInfoData = v
			newList = append(newList, &EdbInfoSearchData{
				EdbDataId: v.EdbDataId,
				DataTime:  v.DataTime,
				Value:     v.Value,
			})
			continue
		}

		// 生成线性方程式
		var a, b float64
		{
			coordinateData := make([]utils.Coordinate, 0)
			tmpCoordinate1 := utils.Coordinate{
				X: 1,
				Y: startEdbInfoData.Value,
			}
			coordinateData = append(coordinateData, tmpCoordinate1)
			tmpCoordinate2 := utils.Coordinate{
				X: float64(betweenDay) + 1,
				Y: v.Value,
			}
			coordinateData = append(coordinateData, tmpCoordinate2)

			a, b = utils.GetLinearResult(coordinateData)
			if math.IsNaN(a) || math.IsNaN(b) {
				err = errors.New("线性方程公式生成失败")
				return
			}
		}

		// 生成对应的值
		{
			for i := 1; i < betweenDay; i++ {
				tmpDataTime := startDataTime.AddDate(0, 0, i)
				aDecimal := decimal.NewFromFloat(a)
				xDecimal := decimal.NewFromInt(int64(i) + 1)
				bDecimal := decimal.NewFromFloat(b)

				val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
				handleDataMap[tmpDataTime.Format(utils.FormatDate)] = val
				newList = append(newList, &EdbInfoSearchData{
					DataTime: tmpDataTime.Format(utils.FormatDate),
					Value:    val,
				})
			}
		}

		// 最后将自己赋值
		newList = append(newList, &EdbInfoSearchData{
			EdbDataId: v.EdbDataId,
			DataTime:  v.DataTime,
			Value:     v.Value,
		})

		startEdbInfoData = v
	}

	return
}

// HandleDataByPreviousData 当前日期无值时,用上一个日期的数据补充当前日期的数据
func HandleDataByPreviousData(edbInfoDataList []*EdbInfoSearchData, handleDataMap map[string]float64) (err error) {
	if len(edbInfoDataList) < 2 {
		return
	}

	var startEdbInfoData *EdbInfoSearchData
	for _, v := range edbInfoDataList {
		handleDataMap[v.DataTime] = v.Value

		// 第一个数据就给过滤了,给后面的试用
		if startEdbInfoData == nil {
			startEdbInfoData = v
			continue
		}

		// 获取两条数据之间相差的天数
		startDataTime, _ := time.ParseInLocation(utils.FormatDate, startEdbInfoData.DataTime, time.Local)
		currDataTime, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
		betweenHour := int(currDataTime.Sub(startDataTime).Hours())
		betweenDay := betweenHour / 24

		// 如果相差一天,那么过滤
		if betweenDay <= 1 {
			startEdbInfoData = v
			continue
		}

		// 生成对应的值
		{
			for i := 1; i < betweenDay; i++ {
				tmpDataTime := startDataTime.AddDate(0, 0, i)
				handleDataMap[tmpDataTime.Format(utils.FormatDate)] = startEdbInfoData.Value
			}
		}

		startEdbInfoData = v
	}

	return
}

// HandleDataByLinearRegressionByTime 插值法补充数据(线性方程式) 时间为日期格式,而不是字符串
func HandleDataByLinearRegressionByTime(edbInfoDataList []*EdbInfoData, handleDataMap map[time.Time]float64) (newList []*EdbInfoData, err error) {
	if len(edbInfoDataList) < 2 {
		return
	}

	var startEdbInfoData *EdbInfoData
	for _, v := range edbInfoDataList {
		handleDataMap[v.DataTime] = v.Value

		// 第一个数据就给过滤了,给后面的试用
		if startEdbInfoData == nil {
			startEdbInfoData = v
			newList = append(newList, &EdbInfoData{
				EdbDataId: v.EdbDataId,
				DataTime:  v.DataTime,
				Value:     v.Value,
			})
			continue
		}

		// 获取两条数据之间相差的天数
		startDataTime := startEdbInfoData.DataTime
		currDataTime := v.DataTime
		betweenHour := int(currDataTime.Sub(startDataTime).Hours())
		betweenDay := betweenHour / 24

		// 如果相差一天,那么过滤
		if betweenDay <= 1 {
			startEdbInfoData = v
			newList = append(newList, &EdbInfoData{
				EdbDataId: v.EdbDataId,
				DataTime:  v.DataTime,
				Value:     v.Value,
			})
			continue
		}

		// 生成线性方程式
		var a, b float64
		{
			coordinateData := make([]utils.Coordinate, 0)
			tmpCoordinate1 := utils.Coordinate{
				X: 1,
				Y: startEdbInfoData.Value,
			}
			coordinateData = append(coordinateData, tmpCoordinate1)
			tmpCoordinate2 := utils.Coordinate{
				X: float64(betweenDay) + 1,
				Y: v.Value,
			}
			coordinateData = append(coordinateData, tmpCoordinate2)

			a, b = utils.GetLinearResult(coordinateData)
			if math.IsNaN(a) || math.IsNaN(b) {
				err = errors.New("线性方程公式生成失败")
				return
			}
		}

		// 生成对应的值
		{
			for i := 1; i < betweenDay; i++ {
				tmpDataTime := startDataTime.AddDate(0, 0, i)
				aDecimal := decimal.NewFromFloat(a)
				xDecimal := decimal.NewFromInt(int64(i) + 1)
				bDecimal := decimal.NewFromFloat(b)

				val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).Round(4).Float64()
				handleDataMap[tmpDataTime] = val
				newList = append(newList, &EdbInfoData{
					DataTime: tmpDataTime,
					Value:    val,
				})
			}
		}

		// 最后将自己赋值
		newList = append(newList, &EdbInfoData{
			EdbDataId: v.EdbDataId,
			DataTime:  v.DataTime,
			Value:     v.Value,
		})

		startEdbInfoData = v
	}

	return
}