package excel

import (
	"encoding/json"
	"errors"
	"eta/eta_api/models/data_manage"
	"eta/eta_api/models/data_manage/excel"
	"eta/eta_api/models/data_manage/excel/request"
	"eta/eta_api/models/data_manage/excel/response"
	"eta/eta_api/models/system"
	"eta/eta_api/services/data"
	"eta/eta_api/services/data/data_manage_permission"
	"eta/eta_api/utils"
	"fmt"
	"sort"
	"time"
)

// GetExcelDetailInfoByExcelInfoId 根据表格id获取表格详情
func GetExcelDetailInfoByExcelInfoId(excelInfoId, sysUserId int, lang string) (excelDetail response.ExcelInfoDetail, errMsg string, err error) {
	errMsg = `获取失败`
	//获取eta表格信息
	excelInfo, err := excel.GetExcelInfoById(excelInfoId)
	if err != nil {
		err = errors.New("获取ETA表格信息失败,Err:" + err.Error())
		if err.Error() == utils.ErrNoRow() {
			errMsg = "ETA表格被删除,请刷新页面"
			err = errors.New("ETA表格被删除,请刷新页面,Err:" + err.Error())
		}
		return
	}

	return formatExcelInfo2Detail(excelInfo, sysUserId, lang)
}

// GetExcelDetailInfoByUnicode 根据表格编码获取表格详情
func GetExcelDetailInfoByUnicode(unicode string, sysUserId int, lang string) (excelDetail response.ExcelInfoDetail, errMsg string, err error) {
	errMsg = `获取失败`
	// 获取eta表格信息
	excelInfo, err := excel.GetExcelInfoByUnicode(unicode)
	if err != nil {
		err = errors.New("获取ETA表格信息失败,Err:" + err.Error())
		if err.Error() == utils.ErrNoRow() {
			errMsg = "ETA表格被删除,请刷新页面"
			err = errors.New("ETA表格被删除,请刷新页面,Err:" + err.Error())
		}
		return
	}

	return formatExcelInfo2Detail(excelInfo, sysUserId, lang)
}

func formatExcelInfo2Detail(excelInfo *excel.ExcelInfo, sysUserId int, lang string) (excelDetail response.ExcelInfoDetail, errMsg string, err error) {
	// 数据权限
	haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(excelInfo.ExcelInfoId, excelInfo.ExcelClassifyId, excelInfo.IsJoinPermission, sysUserId)
	if err != nil {
		err = errors.New("获取表格权限信息失败,Err" + err.Error())
		return
	}

	excelDetail = response.ExcelInfoDetail{
		ExcelInfoId:     excelInfo.ExcelInfoId,
		Source:          excelInfo.Source,
		ExcelType:       excelInfo.ExcelType,
		ExcelName:       excelInfo.ExcelName,
		UniqueCode:      excelInfo.UniqueCode,
		ExcelClassifyId: excelInfo.ExcelClassifyId,
		SysUserId:       excelInfo.SysUserId,
		SysUserRealName: excelInfo.SysUserRealName,
		Content:         excelInfo.Content,
		ExcelImage:      excelInfo.ExcelImage,
		FileUrl:         excelInfo.FileUrl,
		Sort:            excelInfo.Sort,
		IsDelete:        excelInfo.IsDelete,
		ModifyTime:      excelInfo.ModifyTime,
		CreateTime:      excelInfo.CreateTime,
		TableData:       nil,
		HaveOperaAuth:   haveOperaAuth,
	}

	// 无权限,不需要返回数据
	if !haveOperaAuth {
		return
	}

	switch excelInfo.Source {
	case utils.TIME_TABLE: // 时间序列表格
		var tableDataConfig TableDataConfig
		err = json.Unmarshal([]byte(excelDetail.Content), &tableDataConfig)
		if err != nil {
			err = errors.New("表格json转结构体失败,Err:" + err.Error())
			return
		}
		result, tmpErr := GetDataByTableDataConfig(tableDataConfig)
		if tmpErr != nil {
			err = errors.New("获取最新的表格数据失败,Err:" + tmpErr.Error())
			return
		}

		if len(result.EdbInfoIdList) > 0 {
			classifyIdList := make([]int, 0)
			for _, v := range result.Data {
				classifyIdList = append(classifyIdList, v.ClassifyId)
			}

			classifyMap := make(map[int]*data_manage.EdbClassify)

			classifyList, tmpErr := data_manage.GetEdbClassifyByIdList(classifyIdList)
			if tmpErr != nil {
				err = errors.New("获取分类列表失败,Err:" + tmpErr.Error())
				return
			}

			for _, v := range classifyList {
				classifyMap[v.ClassifyId] = v
			}

			// 获取所有有权限的指标和分类
			permissionEdbIdList, permissionClassifyIdList, tmpErr := data_manage_permission.GetUserEdbAndClassifyPermissionList(sysUserId, 0, 0)
			if err != nil {
				err = errors.New("获取所有有权限的指标和分类失败,Err:" + tmpErr.Error())
				return
			}

			for i, v := range result.Data {
				if currClassify, ok := classifyMap[v.ClassifyId]; ok {
					result.Data[i].HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(v.IsJoinPermission, currClassify.IsJoinPermission, v.EdbInfoId, v.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
				}
			}
		}

		excelDetail.TableData = result
	case utils.MIXED_TABLE: // 混合表格
		var result request.MixedTableReq
		err = json.Unmarshal([]byte(excelDetail.Content), &result)
		if err != nil {
			err = errors.New("表格json转结构体失败,Err:" + err.Error())
			return
		}
		newData, tmpErr, tmpErrMsg := GetMixedTableCellData(result, lang)
		if tmpErr != nil {
			errMsg = "获取失败"
			if tmpErrMsg != `` {
				errMsg = tmpErrMsg
			}
			err = errors.New("获取最新的数据失败,Err:" + tmpErr.Error())
			return
		}
		result.Data = newData
		excelDetail.TableData = result
	}
	return
}

// GetExcelInfoOpButton 获取ETA表格的操作权限
func GetExcelInfoOpButton(sysUser *system.Admin, belongUserId, source int, haveOperaAuth bool) (button response.ExcelInfoDetailButton) {
	// 如果没有数据权限,那么直接返回
	if !haveOperaAuth {
		return
	}
	//非管理员角色查看其他用户创建的表格,可刷新、另存为、下载表格;
	button.RefreshButton = true
	button.CopyButton = true
	button.DownloadButton = true

	// 1、本用户创建的表格,可编辑、刷新、另存为、下载、删除,删除需二次确认;
	// 2、管理员角色对所有表格有如上权限;
	// 3、在线excel所有人都能编辑
	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN || sysUser.AdminId == belongUserId || source == utils.EXCEL_DEFAULT {
		button.OpButton = true
		button.DeleteButton = true
	}

	// 自定义分析
	if source == utils.CUSTOM_ANALYSIS_TABLE {
		if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN || sysUser.AdminId == belongUserId {
			button.OpEdbButton = true      // 生成、查看指标按钮
			button.RefreshEdbButton = true // 刷新指标按钮
		}
	}

	return
}

// GetFirstEdbDataList 获取第一列的数据
func GetFirstEdbDataList(edbInfo *data_manage.EdbInfo, num int, manualDateList []string) (resultDataList []request.ManualDataReq, err error) {
	var dataList []*data_manage.EdbDataList
	switch edbInfo.EdbInfoType {
	case 0:
		dataList, err = data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, ``, ``)
	case 1:
		_, dataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(edbInfo.EdbInfoId, ``, ``, false)
	default:
		err = errors.New(fmt.Sprint("获取失败,指标类型异常", edbInfo.EdbInfoType))
	}
	if err != nil {
		return
	}

	// 获取需要的期数
	lenData := len(dataList)
	if lenData <= 0 {
		return
	}

	tmpManualDateNum := 0 // 手工数据的期数
	lenManualDate := len(manualDateList)
	if lenManualDate > 0 {
		sortDateList := manualDateList
		baseDateList := utils.StrArr{}
		baseDateList = append(baseDateList, sortDateList...)
		sort.Sort(baseDateList)
		sortDateList = append([]string{}, baseDateList...)

		lastData := dataList[lenData-1]
		lastDataDate, tmpErr := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
		if tmpErr != nil {
			err = tmpErr
			return
		}

		// 遍历倒序后的日期,然后匹配在实际数据之后日期的个数
		for _, tmpDateStr := range sortDateList {
			tmpDate, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDateStr, time.Local)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			if tmpDate.After(lastDataDate) {
				tmpManualDateNum++
				continue
			}

			break
		}

	}

	// 需要的期数减去手工数据的期数,这才是A列指标需要的数据
	num = num - tmpManualDateNum
	if num > lenData {
		num = lenData
	}

	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, edbInfo.LatestDate, time.Local)
	for i := 1; i <= num; i++ {
		dataTime, _ := time.ParseInLocation(utils.FormatDate, dataList[lenData-i].DataTime, time.Local)
		dataType := 1
		// 如果是预测指标,且当前值的日期,晚于实际日期,那么是预测值
		if edbInfo.EdbInfoType == 1 && dataTime.After(latestDateTime) {
			dataType = 5
		}

		resultDataList = append(resultDataList, request.ManualDataReq{
			DataType:     dataType,
			DataTime:     dataList[lenData-i].DataTime,
			ShowValue:    fmt.Sprint(dataList[lenData-i].Value),
			Value:        fmt.Sprint(dataList[lenData-i].Value),
			DataTimeType: 1,
		})
	}

	return
}

// GetOtherEdbDataList 获取其他列的数据
func GetOtherEdbDataList(edbInfo *data_manage.EdbInfo, dateList []string) (resultDataList []request.ManualDataReq, err error) {
	lenDate := len(dateList)
	if lenDate <= 0 {
		return
	}
	sortDateList := dateList
	baseDateList := utils.StrArr{}
	baseDateList = append(baseDateList, sortDateList...)
	sort.Sort(baseDateList)
	sortDateList = append([]string{}, baseDateList...)

	endDateTime, err := time.ParseInLocation(utils.FormatDate, sortDateList[0], time.Local)
	if err != nil {
		return
	}

	firstDateTime, err := time.ParseInLocation(utils.FormatDate, sortDateList[lenDate-1], time.Local)
	if err != nil {
		return
	}

	var dataList []*data_manage.EdbDataList
	switch edbInfo.EdbInfoType {
	case 0:
		dataList, err = data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, ``, ``)
	case 1:
		_, dataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(edbInfo.EdbInfoId, ``, ``, false)
	default:
		err = errors.New(fmt.Sprint("获取失败,指标类型异常", edbInfo.EdbInfoType))
	}
	if err != nil {
		return
	}

	// 获取日期内的数据(包含开始日期前一个日期,以及 结束日期后一个日期,目的为了做空日期时的 插值法兼容)
	baseDataList := make([]*data_manage.EdbDataList, 0)
	var lastData *data_manage.EdbDataList
	var isInsert bool
	for _, data := range dataList {
		tmpDate := data.DataTime
		tmpDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		if tmpDateTime.Before(firstDateTime) {
			lastData = data
			continue
		}

		// 如果是第一次写入数据
		if !isInsert && lastData != nil {
			baseDataList = append(baseDataList, lastData)
		}

		if tmpDateTime.After(endDateTime) {
			baseDataList = append(baseDataList, data)
			break
		}
		baseDataList = append(baseDataList, data)
		isInsert = true
	}

	// 实际数据的日期map
	realValMap := make(map[string]string)
	for _, v := range baseDataList {
		realValMap[v.DataTime] = v.DataTime
	}

	// 插值法处理
	handleDataMap := make(map[string]float64)
	err = data.HandleDataByLinearRegression(baseDataList, handleDataMap)
	if err != nil {
		return
	}

	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, edbInfo.LatestDate, time.Local)

	// 对于不存在的数据做补充
	for _, date := range sortDateList {
		dataType := 1
		if _, ok := realValMap[date]; !ok {
			dataType = 2
		} else {
			dataTime, _ := time.ParseInLocation(utils.FormatDate, date, time.Local)
			// 如果是预测指标,且当前值的日期,晚于实际日期,那么是预测值
			if edbInfo.EdbInfoType == 1 && dataTime.After(latestDateTime) {
				dataType = 5
			}
		}
		var value, showValue string
		if tmpVal, ok := handleDataMap[date]; ok {
			value = fmt.Sprint(tmpVal)
			showValue = value
		} else {
			dataType = 3
		}
		resultDataList = append(resultDataList, request.ManualDataReq{
			DataType:  dataType,
			DataTime:  date,
			ShowValue: showValue,
			Value:     value,
		})
	}

	return
}

// GetFirstHistoryEdbDataList 获取指标的历史的数据
func GetFirstHistoryEdbDataList(edbInfo *data_manage.EdbInfo, num int, endDate string) (resultDataList []request.ManualDataReq, err error) {
	endDateTime, err := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
	if err != nil {
		return
	}

	var dataList []*data_manage.EdbDataList
	switch edbInfo.EdbInfoType {
	case 0:
		dataList, err = data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, ``, endDate)
	case 1:
		_, dataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(edbInfo.EdbInfoId, ``, endDate, true)
	default:
		err = errors.New(fmt.Sprint("获取失败,指标类型异常", edbInfo.EdbInfoType))
	}
	if err != nil {
		return
	}

	// 获取需要的期数
	lenData := len(dataList)
	if lenData <= 0 {
		return
	}
	lastData := dataList[lenData-1]
	lastDataDateTime, err := time.ParseInLocation(utils.FormatDate, lastData.DataTime, time.Local)
	if err != nil {
		return
	}
	if endDateTime.Equal(lastDataDateTime) || lastDataDateTime.After(endDateTime) {
		dataList = dataList[:lenData-1]
		lenData = len(dataList)
	}
	if num > lenData {
		num = lenData
	}

	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, edbInfo.LatestDate, time.Local)
	for i := 1; i <= num; i++ {
		dataTime, _ := time.ParseInLocation(utils.FormatDate, dataList[lenData-i].DataTime, time.Local)
		dataType := 1
		// 如果是预测指标,且当前值的日期,晚于实际日期,那么是预测值
		if edbInfo.EdbInfoType == 1 && dataTime.After(latestDateTime) {
			dataType = 5
		}

		resultDataList = append(resultDataList, request.ManualDataReq{
			DataType:  dataType,
			DataTime:  dataList[lenData-i].DataTime,
			ShowValue: fmt.Sprint(dataList[lenData-i].Value),
			Value:     fmt.Sprint(dataList[lenData-i].Value),
		})
	}

	return
}

// GetEdbIdsFromExcelCodes 获取表格中的指标IDs
func GetEdbIdsFromExcelCodes(excelCodes []string, sysUserId int, lang string) (edbIds []int, err error) {
	edbIds = make([]int, 0)
	edbIdExist := make(map[int]bool)
	for _, v := range excelCodes {
		// 表格详情
		detail, msg, e := GetExcelDetailInfoByUnicode(v, sysUserId, lang)
		if e != nil {
			err = fmt.Errorf("GetExcelDetailInfoByExcelInfoId err: %s, errMsg: %s", e.Error(), msg)
			return
		}

		// 自定义表格
		if detail.Source == utils.TIME_TABLE {
			jsonByte, e := json.Marshal(detail.TableData)
			if e != nil {
				err = fmt.Errorf("JSON格式化自定义表格数据失败, Err: %s", e.Error())
				return
			}
			var tableData request.TableDataReq
			if e = json.Unmarshal(jsonByte, &tableData); e != nil {
				err = fmt.Errorf("解析自定义表格数据失败, Err: %s", e.Error())
				return
			}
			for _, tv := range tableData.EdbInfoIdList {
				if edbIdExist[tv] {
					continue
				}
				edbIdExist[tv] = true
				edbIds = append(edbIds, tv)
			}
		}

		// 混合表格
		if detail.Source == utils.MIXED_TABLE {
			jsonByte, e := json.Marshal(detail.TableData)
			if e != nil {
				err = fmt.Errorf("JSON格式化混合表格数据失败, Err: %s", e.Error())
				return
			}
			var tableData request.MixedTableReq
			if e = json.Unmarshal(jsonByte, &tableData); e != nil {
				err = fmt.Errorf("解析混合表格数据失败, Err: %s", e.Error())
				return
			}
			if len(tableData.Data) > 0 {
				for _, td := range tableData.Data {
					for _, tv := range td {
						if tv.EdbInfoId > 0 && !edbIdExist[tv.EdbInfoId] {
							edbIdExist[tv.EdbInfoId] = true
							edbIds = append(edbIds, tv.EdbInfoId)
						}
					}
				}
			}
		}
	}

	return
}

// GetExcelEdbBatchRefreshKey 获取批量刷新表格指标缓存key
func GetExcelEdbBatchRefreshKey(source string, reportId, chapterId int) string {
	if source == `` {
		return ``
	}

	return fmt.Sprint("batch_refresh_excel_edb:", source, ":", reportId, ":", chapterId)
}