package area_graph

import (
	"errors"
	"eta/eta_mobile/models/data_manage"
	"eta/eta_mobile/utils"
	"github.com/shopspring/decimal"
	"math"
	"sort"
	"time"
)

type InterpolateStrategy struct{}

// Deal 空值填充：插值法填充
func (i *InterpolateStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
	for _, v := range edbDataList {
		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {

				// 存放补充数据
				var replenishDataList []*data_manage.EdbDataList

				// 处理从 startDate 到第一个数据的日期补充
				if len(dataList) > 0 {
					firstData := dataList[0]
					// 将 startDate 到第一个数据日期之间的自然日填充补充数据，值为 0
					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)

					// 计算两个日期之间的天数差
					if !startDataTime.Equal(firstDataTime) {
						for startDataTime.Before(firstDataTime) {
							// 补充数据
							nextDay := startDataTime.Format(utils.FormatDate)

							// 生成补充数据，值为 0
							replenishIndexData := data_manage.EdbDataList{
								EdbInfoId:     v.EdbInfoId,
								DataTime:      nextDay,
								DataTimestamp: startDataTime.UnixMilli(),
								Value:         0,
							}

							// 将补充数据加入补充数据列表
							replenishDataList = append(replenishDataList, &replenishIndexData)

							// 更新 startDataTime 到下一个日期
							startDataTime = startDataTime.AddDate(0, 0, 1)
						}
					}
				}

				// 插值法补充数据
				var startEdbInfoData *data_manage.EdbDataList
				for index := 0; index < len(dataList); index++ {
					// 获取当前数据和下一个数据
					currentIndexData := dataList[index]
					//afterIndexData := dataList[index+1]

					if startEdbInfoData == nil {
						startEdbInfoData = currentIndexData
						continue
					}

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

					// 如果相差一天,那么过滤
					if betweenDay <= 1 {
						startEdbInfoData = currentIndexData
						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: currentIndexData.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()
						nextDay := tmpDataTime.Format(utils.FormatDate)

						replenishIndexData := data_manage.EdbDataList{
							EdbDataId:     currentIndexData.EdbDataId,
							DataTime:      nextDay,
							DataTimestamp: tmpDataTime.UnixMilli(),
							Value:         val,
						}

						// 将补充数据加入补充数据列表
						replenishDataList = append(replenishDataList, &replenishIndexData)
					}
					startEdbInfoData = currentIndexData
				}

				// 处理从最后一个数据到 endDate 的日期补充
				if len(dataList) > 0 {
					lastData := dataList[len(dataList)-1]
					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据，值为 0
					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)

					// 如果 lastDataTime 不等于 endDate，进行补充
					if !lastDataTime.Equal(endDataTime) {
						// 补充数据直到 endDate
						for lastDataTime.Before(endDataTime) {
							// 补充数据
							addDate := lastDataTime.AddDate(0, 0, 1)
							nextDay := addDate.Format(utils.FormatDate)

							// 生成补充数据，值为 0
							replenishIndexData := data_manage.EdbDataList{
								EdbInfoId:     v.EdbInfoId,
								DataTime:      nextDay,
								DataTimestamp: addDate.UnixMilli(),
								Value:         0,
							}

							// 将补充数据加入补充数据列表
							replenishDataList = append(replenishDataList, &replenishIndexData)

							// 更新 lastDataTime 到下一个日期
							lastDataTime = addDate
						}
					}
				}

				dataList = append(dataList, replenishDataList...)

				// 根据基准指标筛选出符合数据
				var resultDataList []*data_manage.EdbDataList
				for _, dataObject := range dataList {
					if _, ok := standardIndexMap[dataObject.DataTime]; ok {
						// 存在才保留
						resultDataList = append(resultDataList, dataObject)
					}
				}

				// 排序
				sort.Slice(resultDataList, func(i, j int) bool {
					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
				})

				v.DataList = resultDataList
			}
		}
	}
	return nil
}

type FillWithPreviousStrategy struct{}

// Deal 空值填充：前值填充
func (f *FillWithPreviousStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
	// 按自然日补充，再根据基准指标取对应数据
	for _, v := range edbDataList {
		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
				// 存放补充数据
				var replenishDataList []*data_manage.EdbDataList

				// 处理从 startDate 到第一个数据的日期补充
				if len(dataList) > 0 {
					firstData := dataList[0]
					// 将 startDate 到第一个数据日期之间的自然日填充补充数据，值为 0
					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)

					// 计算两个日期之间的天数差
					if !startDataTime.Equal(firstDataTime) {
						for startDataTime.Before(firstDataTime) {
							// 补充数据
							nextDay := startDataTime.Format(utils.FormatDate)

							// 生成补充数据，值为 0
							replenishIndexData := data_manage.EdbDataList{
								EdbInfoId:     v.EdbInfoId,
								DataTime:      nextDay,
								DataTimestamp: startDataTime.UnixMilli(),
								Value:         0,
							}

							// 将补充数据加入补充数据列表
							replenishDataList = append(replenishDataList, &replenishIndexData)

							// 更新 startDataTime 到下一个日期
							startDataTime = startDataTime.AddDate(0, 0, 1)
						}
					}
				}

				// 处理指标中空值数据
				for index := 0; index < len(dataList)-1; index++ {
					// 获取当前数据和下一个数据
					beforeIndexData := dataList[index]
					afterIndexData := dataList[index+1]

					for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
						// 创建补充数据
						nextDay := utils.GetNextDay(beforeIndexData.DataTime)

						toTime := utils.StringToTime(nextDay)
						replenishIndexData := data_manage.EdbDataList{
							EdbInfoId:     v.EdbInfoId,
							DataTime:      nextDay,
							DataTimestamp: toTime.UnixMilli(),
							Value:         beforeIndexData.Value,
						}

						// 将补充数据加入补充数据列表
						replenishDataList = append(replenishDataList, &replenishIndexData)

						// 更新 beforeIndexData 为新创建的补充数据
						beforeIndexData = &replenishIndexData
					}
				}

				// 处理从最后一个数据到 endDate 的日期补充
				if len(dataList) > 0 {
					lastData := dataList[len(dataList)-1]
					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据，值为 0
					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)

					// 如果 lastDataTime 不等于 endDate，进行补充
					if !lastDataTime.Equal(endDataTime) {
						// 补充数据直到 endDate
						for lastDataTime.Before(endDataTime) {
							// 补充数据
							addDate := lastDataTime.AddDate(0, 0, 1)
							nextDay := addDate.Format(utils.FormatDate)

							// 生成补充数据，值为 0
							replenishIndexData := data_manage.EdbDataList{
								EdbInfoId:     v.EdbInfoId,
								DataTime:      nextDay,
								DataTimestamp: addDate.UnixMilli(),
								Value:         0,
							}

							// 将补充数据加入补充数据列表
							replenishDataList = append(replenishDataList, &replenishIndexData)

							// 更新 lastDataTime 到下一个日期
							lastDataTime = addDate
						}
					}
				}

				dataList = append(dataList, replenishDataList...)

				// 根据基准指标筛选出符合数据
				var resultDataList []*data_manage.EdbDataList
				for _, dataObject := range dataList {
					_, ok = standardIndexMap[dataObject.DataTime]
					if ok {
						// 存在才保留
						resultDataList = append(resultDataList, dataObject)
					}
				}

				// 排序
				sort.Slice(resultDataList, func(i, j int) bool {
					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
				})

				v.DataList = resultDataList
			}
		}
	}
	return nil
}

type FillWithNextStrategy struct{}

// Deal 空值填充：后值填充
func (f *FillWithNextStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
	// 按自然日补充，再根据基准指标取对应数据
	for _, v := range edbDataList {
		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {

				// 存放补充数据
				var replenishDataList []*data_manage.EdbDataList

				// 处理从 startDate 到第一个数据的日期补充
				if len(dataList) > 0 {
					firstData := dataList[0]
					// 将 startDate 到第一个数据日期之间的自然日填充补充数据，值为 0
					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)

					// 计算两个日期之间的天数差
					if !startDataTime.Equal(firstDataTime) {
						for !startDataTime.After(firstDataTime) {
							// 补充数据
							nextDay := startDataTime.Format(utils.FormatDate)

							// 生成补充数据，值为 0
							replenishIndexData := data_manage.EdbDataList{
								EdbInfoId:     v.EdbInfoId,
								DataTime:      nextDay,
								DataTimestamp: startDataTime.UnixMilli(),
								Value:         0,
							}

							// 将补充数据加入补充数据列表
							replenishDataList = append(replenishDataList, &replenishIndexData)

							// 更新 startDataTime 到下一个日期
							startDataTime = startDataTime.AddDate(0, 0, 1)
						}
					}
				}

				// 处理从最后一个数据到 endDate 的日期补充
				if len(dataList) > 0 {
					lastData := dataList[len(dataList)-1]
					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据，值为 0
					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)

					// 如果 lastDataTime 不等于 endDate，进行补充
					if !lastDataTime.Equal(endDataTime) {
						// 补充数据直到 endDate
						for lastDataTime.Before(endDataTime) {
							// 补充数据
							addDate := lastDataTime.AddDate(0, 0, 1)
							nextDay := addDate.Format(utils.FormatDate)

							// 生成补充数据，值为 0
							replenishIndexData := data_manage.EdbDataList{
								EdbInfoId:     v.EdbInfoId,
								DataTime:      nextDay,
								DataTimestamp: addDate.UnixMilli(),
								Value:         0,
							}

							// 将补充数据加入补充数据列表
							replenishDataList = append(replenishDataList, &replenishIndexData)

							// 更新 lastDataTime 到下一个日期
							lastDataTime = addDate
						}
					}
				}

				// 将切片数据倒序
				reverseSlice(dataList)

				// 处理指标中空值数据
				for index := 0; index < len(dataList)-1; index++ {
					// 获取当前数据和下一个数据
					beforeIndexData := dataList[index]
					afterIndexData := dataList[index+1]

					for utils.IsMoreThanOneDay(afterIndexData.DataTime, beforeIndexData.DataTime) {
						// 创建补充数据
						nextDay := utils.GetNextDay(afterIndexData.DataTime)

						toTime := utils.StringToTime(nextDay)
						replenishIndexData := data_manage.EdbDataList{
							EdbInfoId:     v.EdbInfoId,
							DataTime:      nextDay,
							DataTimestamp: toTime.UnixMilli(),
							Value:         beforeIndexData.Value,
						}

						// 将补充数据加入补充数据列表
						replenishDataList = append(replenishDataList, &replenishIndexData)

						// 更新 beforeIndexData 为新创建的补充数据
						afterIndexData = &replenishIndexData
					}
				}

				dataList = append(dataList, replenishDataList...)

				// 根据基准指标筛选出符合数据
				var resultDataList []*data_manage.EdbDataList
				for _, dataObject := range dataList {
					_, ok = standardIndexMap[dataObject.DataTime]
					if ok {
						// 存在才保留
						resultDataList = append(resultDataList, dataObject)
					}
				}

				// 排序
				sort.Slice(resultDataList, func(i, j int) bool {
					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
				})

				v.DataList = resultDataList
			}
		}
	}
	return nil
}

type SetToZeroStrategy struct{}

// Deal 空值填充：设为0
func (s *SetToZeroStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) (err error) {
	// 按自然日补充，再根据基准指标取对应数据
	for _, v := range edbDataList {
		if v.EdbInfoId != tmpConfig.StandardEdbInfoId {
			if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
				// 存放补充数据
				var replenishDataList []*data_manage.EdbDataList

				// 处理从 startDate 到第一个数据的日期补充
				if len(dataList) > 0 {
					firstData := dataList[0]
					// 将 startDate 到第一个数据日期之间的自然日填充补充数据，值为 0
					startDataTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
					firstDataTime, _ := time.ParseInLocation(utils.FormatDate, firstData.DataTime, time.Local)

					// 计算两个日期之间的天数差
					if !startDataTime.Equal(firstDataTime) {
						for startDataTime.Before(firstDataTime) {
							// 补充数据
							nextDay := startDataTime.Format(utils.FormatDate)

							// 生成补充数据，值为 0
							replenishIndexData := data_manage.EdbDataList{
								EdbInfoId:     v.EdbInfoId,
								DataTime:      nextDay,
								DataTimestamp: startDataTime.UnixMilli(),
								Value:         0,
							}

							// 将补充数据加入补充数据列表
							replenishDataList = append(replenishDataList, &replenishIndexData)

							// 更新 startDataTime 到下一个日期
							startDataTime = startDataTime.AddDate(0, 0, 1)
						}
					}
				}

				// 处理指标中空值数据
				for index := 0; index < len(dataList)-1; index++ {
					// 获取当前数据和下一个数据
					beforeIndexData := dataList[index]
					afterIndexData := dataList[index+1]

					for utils.IsMoreThanOneDay(beforeIndexData.DataTime, afterIndexData.DataTime) {
						// 创建补充数据
						nextDay := utils.GetNextDay(beforeIndexData.DataTime)

						toTime := utils.StringToTime(nextDay)
						replenishIndexData := data_manage.EdbDataList{
							EdbInfoId:     v.EdbInfoId,
							DataTime:      nextDay,
							DataTimestamp: toTime.UnixMilli(),
							Value:         0,
						}

						// 将补充数据加入补充数据列表
						replenishDataList = append(replenishDataList, &replenishIndexData)

						// 更新 beforeIndexData 为新创建的补充数据
						beforeIndexData = &replenishIndexData
					}
				}

				// 处理从最后一个数据到 endDate 的日期补充
				if len(dataList) > 0 {
					lastData := dataList[len(dataList)-1]
					// 将最后一个数据日期到 endDate 之间的自然日填充补充数据，值为 0
					lastDataTime, _ := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
					endDataTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)

					// 如果 lastDataTime 不等于 endDate，进行补充
					if !lastDataTime.Equal(endDataTime) {
						// 补充数据直到 endDate
						for lastDataTime.Before(endDataTime) {
							// 补充数据
							addDate := lastDataTime.AddDate(0, 0, 1)
							nextDay := addDate.Format(utils.FormatDate)

							// 生成补充数据，值为 0
							replenishIndexData := data_manage.EdbDataList{
								EdbInfoId:     v.EdbInfoId,
								DataTime:      nextDay,
								DataTimestamp: addDate.UnixMilli(),
								Value:         0,
							}

							// 将补充数据加入补充数据列表
							replenishDataList = append(replenishDataList, &replenishIndexData)

							// 更新 lastDataTime 到下一个日期
							lastDataTime = addDate
						}
					}
				}

				dataList = append(dataList, replenishDataList...)

				// 根据基准指标筛选出符合数据
				var resultDataList []*data_manage.EdbDataList
				for _, dataObject := range dataList {
					_, ok = standardIndexMap[dataObject.DataTime]
					if ok {
						// 存在才保留
						resultDataList = append(resultDataList, dataObject)
					}
				}

				// 排序
				sort.Slice(resultDataList, func(i, j int) bool {
					return resultDataList[i].DataTimestamp < resultDataList[j].DataTimestamp
				})

				v.DataList = resultDataList
			}
		}
	}
	return nil
}

type DeleteDateStrategy struct{}

// Deal 删除日期
func (d *DeleteDateStrategy) Deal(tmpConfig data_manage.AreaExtraConf, edbDataList []*data_manage.ChartEdbInfoMapping, standardIndexMap map[string]*data_manage.EdbDataList, startDate string, endDate string) error {
	// 取所有指标的时间交集
	// 创建一个 map 来保存每个时间点的出现次数
	timeMap := make(map[string]int)
	for _, v := range edbDataList {
		if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
			// 遍历所有的 dataList，为每个 DataTime 增加一个计数
			for _, dataObject := range dataList {
				timeMap[dataObject.DataTime]++
			}
		}
	}

	for _, v := range edbDataList {
		if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
			// 遍历所有的 dataList，保留所有时间点在所有指标中都存在的数据
			var resultDataList []*data_manage.EdbDataList
			for _, dataObject := range dataList {
				if timeMap[dataObject.DataTime] == len(edbDataList) {
					// 如果该时间点在所有指标中都存在，加入到结果列表
					resultDataList = append(resultDataList, dataObject)
				}
			}

			// 将符合条件的数据重新赋值回 v.DataList
			v.DataList = resultDataList
		}
	}
	return nil
}

// 将列表颠倒
func reverseSlice(dataList []*data_manage.EdbDataList) {
	// 使用双指针法，前后两个指针向中间逼近
	for i, j := 0, len(dataList)-1; i < j; i, j = i+1, j-1 {
		// 交换位置
		dataList[i], dataList[j] = dataList[j], dataList[i]
	}
}
