package services

import (
	"encoding/json"
	"eta/eta_index_lib/logic"
	"eta/eta_index_lib/models"
	"eta/eta_index_lib/services/alarm_msg"
	"eta/eta_index_lib/utils"
	"fmt"
	"io/ioutil"
	"net/http"
	"sort"
	"strconv"
	"strings"
)

var (
	BridgeApiJiaYueIndexDataUrl = "/api/index_data/jiayue/index_data" // 获取指标数据API
	BridgeApiJiaYueNewIndexUrl  = "/api/index_data/jiayue/new_index"  // 获取增量指标API
	BridgeApiJiaYueMenuListUrl  = "/api/index_data/jiayue/menu_list"  // 获取指标目录API
)

// GetJiaYueIndexDataFromBridge 从桥接服务获取指标数据
func GetJiaYueIndexDataFromBridge(param models.BridgeJiaYueIndexDataParams) (indexData models.BridgeJiaYueIndexAndData, err error) {
	defer func() {
		if err != nil {
			b, _ := json.Marshal(param)
			tips := fmt.Sprintf("桥接服务-获取嘉悦指标数据失败, err: %s, params: %s", err.Error(), string(b))
			utils.FileLog.Info(tips)
			go alarm_msg.SendAlarmMsg(tips, 3)
		}
	}()

	url := fmt.Sprint(utils.EtaBridgeUrl, BridgeApiJiaYueIndexDataUrl)
	data, e := json.Marshal(param)
	if e != nil {
		err = fmt.Errorf("data json marshal err: %s", e.Error())
		return
	}
	body := ioutil.NopCloser(strings.NewReader(string(data)))
	client := &http.Client{}
	req, e := http.NewRequest("POST", url, body)
	if e != nil {
		err = fmt.Errorf("http create request err: %s", e.Error())
		return
	}

	checkToken := utils.MD5(utils.EtaBridgeAppNameEn + utils.EtaBridgeMd5Key)
	contentType := "application/json;charset=utf-8"
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("Authorization", checkToken)
	resp, e := client.Do(req)
	if e != nil {
		err = fmt.Errorf("http client do err: %s", e.Error())
		return
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	b, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		err = fmt.Errorf("resp body read err: %s", e.Error())
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("resp body is empty")
		return
	}
	// 生产环境解密
	if utils.RunMode == "release" {
		str := string(b)
		str = strings.Trim(str, `"`)
		b = utils.DesBase64Decrypt([]byte(str), utils.EtaBridgeDesKey)
	}

	result := new(models.BridgeJiaYueResultIndexData)
	if e = json.Unmarshal(b, &result); e != nil {
		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
		return
	}
	if result.Code != 200 {
		err = fmt.Errorf("result: %s", string(b))
		return
	}
	indexData = result.Data
	return
}

// GetJiaYueNewIndexFromBridge 从桥接服务获取增量指标
func GetJiaYueNewIndexFromBridge() (indexData []models.BridgeJiaYueIndexAndData, err error) {
	defer func() {
		if err != nil {
			tips := fmt.Sprintf("桥接服务-获取嘉悦增量指标失败, err: %s", err.Error())
			utils.FileLog.Info(tips)
			go alarm_msg.SendAlarmMsg(tips, 3)
		}
	}()

	url := fmt.Sprint(utils.EtaBridgeUrl, BridgeApiJiaYueNewIndexUrl)
	body := ioutil.NopCloser(strings.NewReader(""))
	client := &http.Client{}
	req, e := http.NewRequest("POST", url, body)
	if e != nil {
		err = fmt.Errorf("http create request err: %s", e.Error())
		return
	}

	checkToken := utils.MD5(utils.EtaBridgeAppNameEn + utils.EtaBridgeMd5Key)
	contentType := "application/json;charset=utf-8"
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("Authorization", checkToken)
	resp, e := client.Do(req)
	if e != nil {
		err = fmt.Errorf("http client do err: %s", e.Error())
		return
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	b, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		err = fmt.Errorf("resp body read err: %s", e.Error())
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("resp body is empty")
		return
	}
	// 生产环境解密
	if utils.RunMode == "release" {
		str := string(b)
		str = strings.Trim(str, `"`)
		b = utils.DesBase64Decrypt([]byte(str), utils.EtaBridgeDesKey)
	}

	result := new(models.BridgeJiaYueResultNewIndexData)
	if e = json.Unmarshal(b, &result); e != nil {
		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
		return
	}
	if result.Code != 200 {
		err = fmt.Errorf("result: %s", string(b))
		return
	}
	indexData = result.Data
	return
}

// GetJiaYueMenuListFromBridge 从桥接服务获取指标目录列表
func GetJiaYueMenuListFromBridge() (indexData []models.BridgeJiaYueIndexMenuData, err error) {
	defer func() {
		if err != nil {
			tips := fmt.Sprintf("桥接服务-获取嘉悦增量指标失败, err: %s", err.Error())
			utils.FileLog.Info(tips)
			go alarm_msg.SendAlarmMsg(tips, 3)
		}
	}()

	url := fmt.Sprint(utils.EtaBridgeUrl, BridgeApiJiaYueMenuListUrl)
	body := ioutil.NopCloser(strings.NewReader(""))
	client := &http.Client{}
	req, e := http.NewRequest("POST", url, body)
	if e != nil {
		err = fmt.Errorf("http create request err: %s", e.Error())
		return
	}

	checkToken := utils.MD5(utils.EtaBridgeAppNameEn + utils.EtaBridgeMd5Key)
	contentType := "application/json;charset=utf-8"
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("Authorization", checkToken)
	resp, e := client.Do(req)
	if e != nil {
		err = fmt.Errorf("http client do err: %s", e.Error())
		return
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	b, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		err = fmt.Errorf("resp body read err: %s", e.Error())
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("resp body is empty")
		return
	}
	// 生产环境解密
	if utils.RunMode == "release" {
		str := string(b)
		str = strings.Trim(str, `"`)
		b = utils.DesBase64Decrypt([]byte(str), utils.EtaBridgeDesKey)
	}

	result := new(models.BridgeJiaYueResultMenuListData)
	if e = json.Unmarshal(b, &result); e != nil {
		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
		return
	}
	if result.Code != 200 {
		err = fmt.Errorf("result: %s", string(b))
		return
	}
	indexData = result.Data
	return
}

// SyncJiaYueNewIndex 同步嘉悦增量指标
func SyncJiaYueNewIndex(item models.BridgeJiaYueIndexAndData, menus []models.BridgeJiaYueIndexMenuData) (err error) {
	defer func() {
		if err != nil {
			b, _ := json.Marshal(item)
			tips := fmt.Sprintf("SyncJiaYueNewIndex同步增量指标失败, Err: %s\nIndex: %s", err.Error(), string(b))
			utils.FileLog.Info(tips)
			go alarm_msg.SendAlarmMsg(tips, 3)
		}
	}()
	subSource := utils.DATA_SUB_SOURCE_EDB
	// 校验指标来源
	sourceId := models.EdbSourceExtendIdMap[item.SourceType]
	if sourceId <= 0 {
		utils.FileLog.Info(fmt.Sprintf("指标来源ID有误, 忽略同步, sourceId: %d; sourceType: %s", sourceId, item.SourceType))
		return
	}
	edbSource := models.EdbSourceIdMap[sourceId]
	if edbSource == nil {
		utils.FileLog.Info(fmt.Sprintf("指标来源有误, sourceId: %d", sourceId))
		return
	}
	sourceTableName := edbSource.TableName
	if sourceTableName == "" {
		utils.FileLog.Info(fmt.Sprintf("指标数据表有误, 忽略同步, sourceId: %d", sourceId))
		return
	}

	// 指标编码
	indexCode := ""
	if edbSource.EdbCodeRequired == 1 {
		indexCode = item.IndexCode
	} else {
		indexCode = strconv.Itoa(item.Id)
	}
	if indexCode == "" {
		utils.FileLog.Info("指标编码不存在")
		return
	}

	// 查询指标是否已存在, 存在则忽略
	exist, e := models.GetEdbInfoByEdbCode(sourceId, indexCode)
	if e != nil && e.Error() != utils.ErrNoRow() {
		err = fmt.Errorf("查询指标是否存在失败, err: %s", e.Error())
		return
	}
	if exist != nil && exist.EdbInfoId > 0 {
		utils.FileLog.Info(fmt.Sprintf("指标%s已存在, 忽略同步", indexCode))
		return
	}

	// 从桥接服务获取指标和数据
	var params models.BridgeJiaYueIndexDataParams
	params.IndexCode = indexCode
	params.SourceExtend = item.SourceType
	params.StartDate = utils.BASE_START_DATE
	params.EndDate = utils.BASE_END_DATE
	params.IndexCodeRequired = edbSource.EdbCodeRequired
	indexData, e := GetJiaYueIndexDataFromBridge(params)
	if e != nil {
		err = fmt.Errorf("GetJiaYueIndexDataFromBridge err: %s", e.Error())
		return
	}

	// 没找到数据, 频度有误的忽略掉
	if indexData.Id <= 0 {
		utils.FileLog.Info(fmt.Sprintf("SyncJiaYueNewIndex indexCode empty, IndexCode: %s", indexCode))
		return
	}
	frequency := TransJiaYueFrequency(item.Frequency)
	if frequency == "" {
		utils.FileLog.Info(fmt.Sprintf("SyncJiaYueNewIndex frequency empty, Frequency: %s", item.Frequency))
		return
	}

	// 获取指标目录对应的所有父级目录
	parentId := 0
	parentMenus := GetJiaYueParentMenusByMenu(indexData.MenuData, menus, 0)
	level := len(parentMenus)
	if level > 0 {
		// 将父级目录按照Level倒序排序(因为是从子目录往上找的, 所以Level最大的对应为顶级目录), 再进行遍历
		sort.Slice(parentMenus, func(i, j int) bool {
			return parentMenus[i].Level > parentMenus[j].Level
		})
		for _, p := range parentMenus {
			l := level - p.Level - 1
			c, _, msg := models.SaveEdbClassify(p.Menu.Name, parentId, l, 0, utils.InitAdminId, utils.InitAdminName)
			if msg != "" {
				err = fmt.Errorf("SaveEdbClassify err: %s", msg)
				return
			}
			parentId = c.ClassifyId
		}
	}
	classify, _, msg := models.SaveEdbClassify(indexData.MenuData.Name, parentId, level, 0, utils.InitAdminId, utils.InitAdminName)
	if msg != "" {
		err = fmt.Errorf("SaveEdbClassify err: %s", msg)
		return
	}

	// 新增指标
	addParams := new(models.AddEdbInfoParams)
	addParams.EdbCode = indexCode
	addParams.EdbName = item.IndexName
	addParams.Frequency = frequency
	addParams.Unit = item.Unit
	addParams.ClassifyId = classify.ClassifyId
	addParams.Source = sourceId
	adminId, _ := strconv.Atoi(utils.InitAdminId)
	edbInfo, e := models.EdbInfoAdd(addParams, "", adminId, utils.InitAdminName)
	if e != nil {
		err = fmt.Errorf("EdbInfoAdd err: %s", e.Error())
		return
	}

	// 刷新指标数据
	e = models.RefreshEdbDataFromJiaYue(sourceId, subSource, edbInfo.EdbInfoId, sourceTableName, edbInfo.EdbCode, params.StartDate, indexData.IndexData)
	if e != nil {
		err = fmt.Errorf("RefreshEdbDataFromJiaYue err: %s", e.Error())
		return
	}

	// 更新指标最大最小值
	e, errMsg := models.UnifiedModifyEdbInfoMaxAndMinInfo(edbInfo)
	if e != nil {
		err = fmt.Errorf("UnifiedModifyEdbInfoMaxAndMinInfo err: %s, errMsg: %s", e.Error(), errMsg)
		return
	}

	// 更新ES
	go logic.UpdateEs(edbInfo.EdbInfoId)

	return
}

// TransJiaYueFrequency 频度转换
func TransJiaYueFrequency(origin string) string {
	mapping := map[string]string{
		"日":   "日度",
		"周":   "周度",
		"旬":   "旬度",
		"半月":  "旬度",
		"月":   "月度",
		"季":   "季度",
		"半年":  "半年度",
		"年":   "年度",
		"日度":  "日度",
		"周度":  "周度",
		"旬度":  "旬度",
		"月度":  "月度",
		"季度":  "季度",
		"半年度": "半年度",
		"年度":  "年度",
	}
	return mapping[origin]
}

// GetJiaYueParentMenusByMenu 获取指定目录的父级目录
func GetJiaYueParentMenusByMenu(menu models.BridgeJiaYueIndexMenuData, menus []models.BridgeJiaYueIndexMenuData, level int) (results []models.BridgeJiaYueIndexMenuWithLevel) {
	results = make([]models.BridgeJiaYueIndexMenuWithLevel, 0)
	for _, m := range menus {
		if menu.ParentId == m.Id {
			results = append(results, models.BridgeJiaYueIndexMenuWithLevel{
				Level: level,
				Menu:  m,
			})
			ps := GetJiaYueParentMenusByMenu(m, menus, level+1)
			if len(ps) > 0 {
				results = append(results, ps...)
			}
		}
	}
	return
}