package line_feature

import (
	"errors"
	"eta/eta_api/models/data_manage"
	"eta/eta_api/models/data_manage/line_feature"
	"eta/eta_api/models/data_manage/line_feature/response"
	"eta/eta_api/models/system"
	"eta/eta_api/services/data"
	"eta/eta_api/utils"
	"github.com/shopspring/decimal"
	"strconv"
	"strings"
	"time"
)

// GetStandardDeviationData 获取标准差图表的指标数据
func GetStandardDeviationData(chartInfoId int, startDate, endDate string, mappingInfo *data_manage.ChartEdbInfoMapping, calculateValue int) (edbList []*data_manage.ChartEdbInfoMapping, dataResp response.LineFeatureDataResp, err error, errMsg string) {
	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)

	// 指标对应的所有数据
	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo}, "")
	if err != nil {
		return
	}

	if len(edbList) != 1 {
		errMsg = `指标异常`
		err = errors.New(errMsg)
		return
	}

	edb := edbList[0]
	dataList := edb.DataList.([]*data_manage.EdbDataList)
	newDataList := make([]data_manage.EdbDataList, 0)
	lenData := len(dataList)

	var minVal, maxVal float64
	if lenData >= calculateValue {
		tmpDataList := make([]float64, 0)
		for _, tmpData := range dataList {
			tmpDataList = append(tmpDataList, tmpData.Value)
		}
		for i := calculateValue; i <= lenData; i++ {
			tmpV := utils.CalculateStandardDeviation(tmpDataList[i-calculateValue : i])
			tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
			newDataList = append(newDataList, data_manage.EdbDataList{
				EdbDataId:     i,
				EdbInfoId:     edb.EdbInfoId,
				DataTime:      dataList[i-1].DataTime,
				DataTimestamp: dataList[i-1].DataTimestamp,
				Value:         tmpV,
			})
			if tmpV > maxVal {
				maxVal = tmpV
			}
			if tmpV < minVal {
				minVal = tmpV
			}
		}
	}

	dataResp = response.LineFeatureDataResp{
		MaxData:             maxVal,
		MinData:             minVal,
		LatestDate:          edb.LatestDate,
		EdbInfoCategoryType: edb.EdbInfoCategoryType,
		ChartColor:          `#00F`,
		ChartStyle:          `spline`,
		PredictChartColor:   `#00F`,
		ChartType:           0,
		ChartWidth:          3,
		EdbName:             "标准差",
		EdbNameEn:           "standard deviation",
		Unit:                edb.Unit,
		UnitEn:              edb.UnitEn,
		IsAxis:              1,
		DataList:            newDataList,
	}

	return
}

// GetPercentileData 获取百分位图表的指标数据
func GetPercentileData(chartInfoId int, startDate, endDate string, mappingInfo *data_manage.ChartEdbInfoMapping, calculateValue int, calculateUnit string) (edbList []*data_manage.ChartEdbInfoMapping, dataResp response.LineFeatureDataResp, err error, errMsg string) {
	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
	moveUnitDays, ok := utils.FrequencyDaysMap[calculateUnit]
	if !ok {
		errMsg = `错误的周期`
		err = errors.New(errMsg)
		return
	}
	calculateDay := calculateValue * moveUnitDays
	// 指标对应的所有数据
	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo}, "")
	if err != nil {
		return
	}

	if len(edbList) != 1 {
		errMsg = `指标异常`
		err = errors.New(errMsg)
		return
	}

	edb := edbList[0]
	dataList := edb.DataList.([]*data_manage.EdbDataList)
	newDataList := make([]data_manage.EdbDataList, 0)

	var edbMinVal, edbMaxVal float64
	dataMap := make(map[time.Time]float64, 0)
	for _, tmpData := range dataList {
		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
		dataMap[currDateTime] = tmpData.Value
	}

	//百分位：对所选指标滚动地取对应时间长度的数据值，取最大值Max，最小值Min，计算Max-Min，百分位=(现值-Min)/(Max-Min)，Max=Min时不予计算。
	for i, tmpData := range dataList {
		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
		maxVal := tmpData.Value
		minVal := tmpData.Value
		for k := 0; k < calculateDay; k++ {
			preVal, ok2 := dataMap[currDateTime.AddDate(0, 0, -k)]
			if ok2 {
				if preVal > maxVal {
					maxVal = preVal
				}
				if preVal < minVal {
					minVal = preVal
				}
			}
		}

		if maxVal == minVal {
			continue
		}
		//百分位=(现值-Min)/(Max-Min)
		tmpV := (tmpData.Value - minVal) / (maxVal - minVal) * 100
		tmpV, _ = decimal.NewFromFloat(tmpV).Round(4).Float64()
		newDataList = append(newDataList, data_manage.EdbDataList{
			EdbDataId:     i,
			EdbInfoId:     edb.EdbInfoId,
			DataTime:      dataList[i].DataTime,
			DataTimestamp: dataList[i].DataTimestamp,
			Value:         tmpV,
		})

		if tmpV < edbMinVal {
			edbMinVal = tmpV
		}
		if tmpV > edbMaxVal {
			edbMaxVal = tmpV
		}
	}

	dataResp = response.LineFeatureDataResp{
		MaxData:             edbMaxVal,
		MinData:             edbMinVal,
		LatestDate:          edb.LatestDate,
		EdbInfoCategoryType: edb.EdbInfoCategoryType,
		ChartColor:          `#00F`,
		ChartStyle:          `spline`,
		PredictChartColor:   `#00F`,
		ChartType:           0,
		ChartWidth:          3,
		EdbName:             "百分位",
		EdbNameEn:           "percentile",
		Unit:                "%",
		UnitEn:              "%",
		IsAxis:              1,
		DataList:            newDataList,
	}

	return
}

// GetFrequencyDistributionData 获取频率分布的图表数据
func GetFrequencyDistributionData(chartInfoId int, mappingInfo *data_manage.ChartEdbInfoMapping, dateType, stepVal int, startDate, endDate string) (edbList []*data_manage.ChartEdbInfoMapping, dataResp response.FrequencyDistributionResp, err error, errMsg string) {
	XDataList := make([]float64, 0)
	// 频度
	Y1DataList := make([]response.FrequencyDistributionYData, 0)
	// 累计频率
	Y2DataList := make([]response.FrequencyDistributionYData, 0)
	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)

	// 指标对应的所有数据
	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo}, "")
	if err != nil {
		return
	}
	if len(edbList) != 1 {
		err = errors.New("指标异常")
		return
	}

	startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
	var endDateTime time.Time
	if endDate != `` {
		endDateTime, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
	}

	edb := edbList[0]
	dataList := edb.DataList.([]*data_manage.EdbDataList)
	if len(dataList) <= 0 {
		return
	}

	// 非自定义
	if dateType != 8 {
		endDate = dataList[len(dataList)-1].DataTime
		endDateTime, err = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
		if err != nil {
			return
		}
		//日期类型:1:最近3月；2:最近6月；3:最近1年；4:最近2年；5:最近3年；6:最近5年；7:最近10年，8:自定义时间
		startDateTime = utils.GetDateByDateType2(dateType, endDateTime)
		startDate = startDateTime.Format(utils.FormatDate)

		newDataList := make([]*data_manage.EdbDataList, 0)
		for _, v := range dataList {
			tmpDataTime, tmpErr := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			if tmpDataTime.Equal(startDateTime) || tmpDataTime.After(startDateTime) {
				newDataList = append(newDataList, v)
			}

		}
		dataList = newDataList
	}

	maxVal := dataList[0].Value
	minVal := dataList[0].Value
	dataValMap := make(map[float64]int)
	total := 0 // 数据总量
	for _, tmpData := range dataList {
		currDateTime, _ := time.ParseInLocation(utils.FormatDate, tmpData.DataTime, time.Local)
		if (currDateTime.Equal(startDateTime) || currDateTime.After(startDateTime)) && (endDateTime.IsZero() || currDateTime.Before(endDateTime)) {
			if maxVal < tmpData.Value {
				maxVal = tmpData.Value
			}
			if minVal > tmpData.Value {
				minVal = tmpData.Value
			}

			num, ok := dataValMap[tmpData.Value]
			if ok {
				dataValMap[tmpData.Value] = num + 1
			} else {
				dataValMap[tmpData.Value] = 1
			}
			total++
		}
	}

	if total <= 0 {
		errMsg = `没有数据`
		err = errors.New(errMsg)
		return
	}

	// 最大最小值 向上/下取整
	minVal = utils.GetFloorNewNum(minVal, 2)
	maxVal = utils.GetCeilNewNum(maxVal, 2)
	//间距
	spacing, _ := (decimal.NewFromFloat(maxVal).Sub(decimal.NewFromFloat(minVal))).Div(decimal.NewFromInt(int64(stepVal))).Float64()

	distributionDataNumMap := make(map[float64]int)
	for i := 1; i <= stepVal; i++ {
		tmpMinVal, _ := decimal.NewFromFloat(minVal).Add((decimal.NewFromFloat(spacing)).Mul(decimal.NewFromInt(int64(i - 1)))).Float64()
		tmpMaxVal, _ := decimal.NewFromFloat(minVal).Add((decimal.NewFromFloat(spacing)).Mul(decimal.NewFromInt(int64(i)))).Float64()
		XDataList = append(XDataList, tmpMinVal)
		distributionDataNumMap[tmpMinVal] = 0
		for tmpVal, num := range dataValMap {
			if tmpMinVal <= tmpVal {
				// 最后一期数据是要小于等于
				if i == stepVal {
					if tmpVal <= tmpMaxVal {
						distributionDataNumMap[tmpMinVal] += num
					}
				} else {
					if tmpVal < tmpMaxVal {
						distributionDataNumMap[tmpMinVal] += num
					}
				}
			}
		}
	}

	var minFrequency, maxFrequency float64
	tmpNum := 0
	for k, tmpMinVal := range XDataList {
		// 数量
		frequencyYNum := distributionDataNumMap[tmpMinVal]

		// 频率
		tmpFrequency, _ := decimal.NewFromInt(int64(frequencyYNum)).Div(decimal.NewFromInt(int64(total))).Mul(decimal.NewFromInt(100)).Round(4).Float64()
		Y1DataList = append(Y1DataList, response.FrequencyDistributionYData{
			X: tmpMinVal,
			Y: tmpFrequency,
		})
		if k == 0 {
			minFrequency = tmpFrequency
			maxFrequency = tmpFrequency
		} else {
			if tmpFrequency < minFrequency {
				minFrequency = tmpFrequency
			}
			if tmpFrequency > maxFrequency {
				maxFrequency = tmpFrequency
			}
		}

		// 累计数
		tmpNum += frequencyYNum
		// 累计频率
		tmpTotalFrequency, _ := decimal.NewFromInt(int64(tmpNum)).Div(decimal.NewFromInt(int64(total))).Mul(decimal.NewFromInt(100)).Round(4).Float64()
		Y2DataList = append(Y2DataList, response.FrequencyDistributionYData{
			X: tmpMinVal,
			Y: tmpTotalFrequency,
		})
	}

	newDataList := []response.FrequencyDistributionData{
		{
			Name:   "频率",
			NameEn: "Frequency",
			Unit:   "%",
			UnitEn: "%",
			Value:  Y1DataList,
			Color:  "#00F",
			IsAxis: 1,
		}, {
			Name:   "累计频率",
			NameEn: "Total Frequency",
			Unit:   "%",
			UnitEn: "%",
			Value:  Y2DataList,
			Color:  "#F00",
			IsAxis: 0,
		},
	}
	edbList[0].DataList = nil
	dataResp = response.FrequencyDistributionResp{
		LeftMinValue:  minFrequency,
		LeftMaxValue:  maxFrequency,
		RightMinValue: 0,
		RightMaxValue: 100,
		DataList:      newDataList,
	}
	return
}

// AddChartInfo 添加图表
func AddChartInfo(req data_manage.AddChartInfoReq, edbInfoMapping *data_manage.ChartEdbInfoMapping, source int, sysUser *system.Admin) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
	isSendEmail = true

	req.ChartName = strings.Trim(req.ChartName, " ")
	if req.ChartName == "" {
		errMsg = "请填写图表名称！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	if req.ChartClassifyId <= 0 {
		errMsg = "分类参数错误！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}

	chartClassify, err := data_manage.GetChartClassifyById(req.ChartClassifyId)
	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 chartClassify == nil {
		errMsg = "分类不存在"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}

	edbInfoIdArr := []int{edbInfoMapping.EdbInfoId}
	var edbInfoIdArrStr []string
	for _, v := range edbInfoIdArr {
		edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v))
	}
	edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",")
	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
	}

	disableVal := data.CheckIsDisableChart(edbInfoIdArr)

	chartInfo = new(data_manage.ChartInfo)
	chartInfo.ChartName = req.ChartName
	chartInfo.EdbInfoIds = edbInfoIdStr
	chartInfo.ChartClassifyId = req.ChartClassifyId
	chartInfo.SysUserId = sysUser.AdminId
	chartInfo.SysUserRealName = sysUser.RealName
	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 = req.DateType
	chartInfo.StartDate = req.StartDate
	chartInfo.EndDate = req.EndDate
	chartInfo.SeasonStartDate = req.StartDate
	chartInfo.SeasonEndDate = req.EndDate
	chartInfo.ChartImage = req.ChartImage
	chartInfo.LeftMin = req.LeftMin
	chartInfo.LeftMax = req.LeftMax
	chartInfo.RightMin = req.RightMin
	chartInfo.RightMax = req.RightMax
	chartInfo.Disabled = disableVal
	chartInfo.Source = source
	chartInfo.ExtraConfig = req.ExtraConfig

	// 指标信息
	mapList := make([]*data_manage.ChartEdbMapping, 0)
	{
		mapItem := new(data_manage.ChartEdbMapping)
		mapItem.EdbInfoId = edbInfoMapping.EdbInfoId
		mapItem.CreateTime = time.Now()
		mapItem.ModifyTime = time.Now()
		mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + strconv.Itoa(edbInfoMapping.EdbInfoId))
		mapItem.IsOrder = true
		mapItem.IsAxis = 1
		mapItem.EdbInfoType = 1
		mapItem.Source = utils.CHART_SOURCE_CORRELATION
		mapList = append(mapList, mapItem)
	}

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

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

	return
}

// EditChartInfo 编辑图表
func EditChartInfo(req data_manage.EditChartInfoReq, edbInfoMapping *data_manage.ChartEdbInfoMapping, sysUser *system.Admin) (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 !utils.InArrayByInt([]int{utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION, utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE, utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY}, chartItem.Source) {
		errMsg = "该图不是统计分析图表！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}

	req.ChartName = strings.Trim(req.ChartName, " ")
	if req.ChartClassifyId <= 0 {
		errMsg = "分类参数错误！"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}
	chartClassify, err := data_manage.GetChartClassifyById(req.ChartClassifyId)
	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 chartClassify == nil {
		errMsg = "分类不存在"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}

	// 图表操作权限
	ok := data.CheckOpChartPermission(sysUser, chartItem.SysUserId)
	if !ok {
		errMsg = "没有该图表的操作权限"
		err = errors.New(errMsg)
		isSendEmail = false
		return
	}

	edbInfoIdArr := []int{edbInfoMapping.EdbInfoId}
	var edbInfoIdArrStr []string
	for _, v := range edbInfoIdArr {
		edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v))
	}
	edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",")

	//判断图表是否存在
	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
	}

	// 图表启用与否
	disableVal := data.CheckIsDisableChart(edbInfoIdArr)

	// 修改图表与指标mapping
	err = line_feature.EditLineFeatureChartAndMapping(&req, edbInfoIdStr, "公历", req.DateType, disableVal, req.ExtraConfig)
	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
}

// CopyChartInfo 复制图表
func CopyChartInfo(configId, configSource, classifyId int, chartName string, edbInfoMapping *data_manage.ChartEdbInfoMapping, oldChartInfo *data_manage.ChartInfo, sysUser *system.Admin) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
	isSendEmail = true

	multipleGraphConfig, err := data_manage.GetMultipleGraphConfigById(configId)
	if err != nil {
		return
	}
	multipleGraphConfig.MultipleGraphConfigId = 0
	err = data_manage.AddMultipleGraphConfig(multipleGraphConfig)
	if err != nil {
		return
	}

	// 添加图
	addChartReq := data_manage.AddChartInfoReq{
		ChartClassifyId: classifyId,
		ChartName:       chartName,
		ChartType:       utils.CHART_TYPE_CURVE,
		Calendar:        "公历",
		ExtraConfig:     oldChartInfo.ExtraConfig,
		ChartImage:      oldChartInfo.ChartImage,
	}
	chartSource := oldChartInfo.Source // 默认是相关性图
	chartInfo, err, errMsg, isSendEmail = AddChartInfo(addChartReq, edbInfoMapping, chartSource, sysUser)
	if err != nil {
		return
	}

	// 添加关系
	multipleGraphConfigChartMapping := &data_manage.MultipleGraphConfigChartMapping{
		//Id:                    0,
		MultipleGraphConfigId: multipleGraphConfig.MultipleGraphConfigId,
		ChartInfoId:           chartInfo.ChartInfoId,
		Source:                configSource,
		ModifyTime:            time.Now(),
		CreateTime:            time.Now(),
	}
	err = data_manage.AddMultipleGraphConfigChartMapping(multipleGraphConfigChartMapping)
	if err != nil {
		return
	}

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

	return
}
