package misc

import (
	"fmt"
	"math"
	"reflect"
	"sort"
	"sync"
	"time"

	"git.rosy.net.cn/baseapi"
	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/jx-callback/business/jxstore/cms"
	"git.rosy.net.cn/jx-callback/business/jxstore/permission"
	"git.rosy.net.cn/jx-callback/business/jxutils"
	"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
	"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
	"git.rosy.net.cn/jx-callback/globals"
	"git.rosy.net.cn/jx-callback/globals/refutil"
)

const (
	EnableScheduleScoreStore = true
	ParallelCount            = 1

	GoldMedalScore   = 90
	SilverMedalScore = 80
	BronzeMedalScore = 70
	GoldMedalLevel   = 1
	SilverMedalLevel = 2
	BronzeMedalLevel = 3

	ItemTotalScore = 10

	StoreOpenTimeNormalTime     = 12.0                                         //小时
	SaleSkuNormalCount          = 1000                                         //数量
	SaleSkuScorePerUnit         = float64(ItemTotalScore) / SaleSkuNormalCount //分数
	PromotionSkuNormalCount     = 20                                           //数量
	AveragePickupTimeNormalTime = 10.0                                         //分钟
	BadCommentOrderNormalRatio  = 0.2                                          //百分比
	UnfinishOrderNormalRatio    = 1.0                                          //百分比
	AbsentGoodsOrderNormalRatio = 1.0                                          //百分比
	StoreRangeGoodRadius        = 2.0                                          //千米
	StoreRangeBadRadius         = 1.0                                          //千米
	SaleSkuPriceRatio           = 90                                           //千米
	SaleSkuCheckRange           = 5.0                                          //千米

	WeekNum = 5 //得到门店近期周的数量
)

var (
	scoreStoreTimeList = []string{
		"23:00:00",
	}
	scoreStoreCheckTimeEnd = "23:30:00"
	fullVendorList         = map[int]bool{
		model.VendorIDJD:   true,
		model.VendorIDMTWM: true,
		model.VendorIDEBAI: true,
	}
	storeScoreFieldName = []string{
		model.FieldStoreOpenTime,
		model.FieldSaleSkuCount,
		model.FieldAveragePickupTime,
		model.FieldBadCommentOrder,
		model.FieldUnfinishOrder,
		model.FieldAbsentGoodsOrder,
		model.FieldPromotionSku,
		model.FieldFullVendor,
		model.FieldStoreRange,
		model.FieldSaleSkuPrice,
	}

	storeScoreDataWrapper StoreScoreDataWrapper
	allStoreSkusWrapper   AllStoreSkusWrapper
	scoreDate             time.Time
	isScoring             bool
)

type AllStoreSkusWrapper struct {
	allStoreSkus map[int]map[int]int
	locker       sync.RWMutex
}

func (a *AllStoreSkusWrapper) InitData() {
	a.allStoreSkus = make(map[int]map[int]int)
}

func (a *AllStoreSkusWrapper) ClearData() {
	a.allStoreSkus = nil
}

func (a *AllStoreSkusWrapper) SetData(storeID int, skuMapData map[int]int) {
	a.locker.Lock()
	defer a.locker.Unlock()
	a.allStoreSkus[storeID] = skuMapData
}

func (a *AllStoreSkusWrapper) GetData(storeID int) map[int]int {
	a.locker.RLock()
	defer a.locker.RUnlock()
	return a.allStoreSkus[storeID]
}

type StoreScoreDataWrapper struct {
	storeScoreData             map[int]*model.StoreScore
	dailyBadCommentOrderCount  map[int]int
	dailyUnFinishOrderCount    map[int]int
	dailyFinishOrderCount      map[int]int
	dailyAbsentGoodsOrderCount map[int]int
	locker                     sync.RWMutex
}

func (s *StoreScoreDataWrapper) InitData() {
	s.storeScoreData = make(map[int]*model.StoreScore)
	s.dailyBadCommentOrderCount = make(map[int]int)
	s.dailyUnFinishOrderCount = make(map[int]int)
	s.dailyFinishOrderCount = make(map[int]int)
	s.dailyAbsentGoodsOrderCount = make(map[int]int)
}

func (s *StoreScoreDataWrapper) ClearData() {
	s.storeScoreData = nil
	s.dailyBadCommentOrderCount = nil
	s.dailyUnFinishOrderCount = nil
	s.dailyFinishOrderCount = nil
	s.dailyAbsentGoodsOrderCount = nil
}

func (s *StoreScoreDataWrapper) SetData(storeID int, valueName string, value int) {
	s.locker.Lock()
	defer s.locker.Unlock()
	data := s.storeScoreData[storeID]
	if data == nil {
		data = &model.StoreScore{}
		data.StoreID = storeID
		data.ScoreDate = scoreDate
		s.storeScoreData[storeID] = data
	}
	valueInfo := reflect.ValueOf(data).Elem()
	valueInfo.FieldByName(valueName).SetInt(int64(value))
}

func (s *StoreScoreDataWrapper) SetDailyBadCommentOrderCount(storeCountList []*model.StoreCount) {
	s.locker.Lock()
	defer s.locker.Unlock()
	for _, value := range storeCountList {
		s.dailyBadCommentOrderCount[value.StoreID] = value.Count
	}
}

func (s *StoreScoreDataWrapper) GetDailyBadCommentOrderCount(storeID int) int {
	s.locker.RLock()
	defer s.locker.RUnlock()
	if count, ok := s.dailyBadCommentOrderCount[storeID]; ok {
		return count
	}

	return 0
}

func (s *StoreScoreDataWrapper) SetDailyUnFinishOrderCount(storeCountList []*model.StoreCount) {
	for _, value := range storeCountList {
		s.dailyUnFinishOrderCount[value.StoreID] = value.Count
	}
}

func (s *StoreScoreDataWrapper) GetDailyUnFinishOrderCount(storeID int) int {
	s.locker.RLock()
	defer s.locker.RUnlock()
	if count, ok := s.dailyUnFinishOrderCount[storeID]; ok {
		return count
	}

	return 0
}

func (s *StoreScoreDataWrapper) SetDailyFinishOrderCount(storeCountList []*model.StoreCount) {
	for _, value := range storeCountList {
		s.dailyFinishOrderCount[value.StoreID] = value.Count
	}
}

func (s *StoreScoreDataWrapper) GetDailyFinishOrderCount(storeID int) int {
	s.locker.RLock()
	defer s.locker.RUnlock()
	if count, ok := s.dailyFinishOrderCount[storeID]; ok {
		return count
	}

	return 0
}

func (s *StoreScoreDataWrapper) SetDailyAbsentGoodsOrderCount(storeCountList []*model.StoreCount) {
	for _, value := range storeCountList {
		s.dailyAbsentGoodsOrderCount[value.StoreID] = value.Count
	}
}

func (s *StoreScoreDataWrapper) GetDailyAbsentGoodsOrderCount(storeID int) int {
	s.locker.RLock()
	defer s.locker.RUnlock()
	if count, ok := s.dailyAbsentGoodsOrderCount[storeID]; ok {
		return count
	}

	return 0
}

func (s *StoreScoreDataWrapper) InsertStoreScore() {
	for _, value := range s.storeScoreData {
		dao.InsertStoreScore(value)
	}
}

//得到所有门店的可售商品(优化内存，只存商品的价格)
func GetAllStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeList []*cms.StoreExt) {
	allStoreSkusWrapper.InitData()
	taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
		storeInfo := batchItemList[0].(*cms.StoreExt)
		storeID := storeInfo.ID
		if jxSkuInfoData, err2 := cms.GetStoreSkus(ctx, storeID, []int{}, true, "", true, false, map[string]interface{}{}, 0, -1); jxSkuInfoData != nil {
			jxSkuPriceMapData := make(map[int]int)
			for _, value := range jxSkuInfoData.SkuNames {
				for _, skuInfo := range value.Skus {
					saleStatus := jxutils.MergeSkuStatus(skuInfo.SkuStatus, skuInfo.StoreSkuStatus)
					if saleStatus == model.SkuStatusNormal {
						jxSkuPriceMapData[skuInfo.SkuID] = skuInfo.BindPrice
					}
				}
			}
			allStoreSkusWrapper.SetData(storeID, jxSkuPriceMapData)
		} else {
			globals.SugarLogger.Warnf("store_score.GetAllStoreSkus %d return empty, err:%v", storeID, err2)
		}
		return retVal, err
	}
	taskParallel := tasksch.NewParallelTask("得到所有门店商品", tasksch.NewParallelConfig().SetParallelCount(ParallelCount), ctx, taskFunc, storeList)
	tasksch.HandleTask(taskParallel, parentTask, true).Run()
	taskParallel.GetResult(0)
}

func GetOpenTime(opTimeList []int16) (opTime float64) {
	opTime = 0
	for index, _ := range opTimeList {
		if index%2 == 1 {
			endTime := opTimeList[index]
			startTime := opTimeList[index-1]
			diffHour := float64(endTime/100 - startTime/100)
			diffMin := float64(endTime%100-startTime%100) / 60
			opTime += diffHour + diffMin
		}
	}
	return opTime
}

//营业时间12小时及以上满分，总分10分，每少一个小时扣1分
func ScoreStoreOpenTime(storeInfo *cms.StoreExt) {
	storeID := storeInfo.ID
	storeStatus := storeInfo.Status
	isStoreOpen := storeStatus == model.StoreStatusOpened
	finalScore := 0
	if isStoreOpen {
		for _, storeMap := range storeInfo.StoreMaps {
			isSyncStoreSku := storeMap.IsSync
			vendorStoreStatus := storeMap.Status
			isVendorStoreOpen := vendorStoreStatus == model.StoreStatusOpened
			opTimeList := storeInfo.GetOpTimeList()
			if len(opTimeList) > 0 && isStoreOpen && isVendorStoreOpen && isSyncStoreSku != 0 {
				opTime := GetOpenTime(opTimeList)
				if opTime >= StoreOpenTimeNormalTime {
					finalScore = ItemTotalScore
				} else {
					decScore := int(math.Round(StoreOpenTimeNormalTime - opTime))
					finalScore = ItemTotalScore - decScore
					if finalScore < 0 {
						finalScore = 0
					}
				}
			}
		}
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldStoreOpenTime, finalScore)
}

//可售商品数量大于1000，总分10分，按比例扣
func ScoreSaleSkuCount(storeInfo *cms.StoreExt) {
	storeID := storeInfo.ID
	skusMapData := allStoreSkusWrapper.GetData(storeID)
	finalScore := 0
	if len(skusMapData) > 0 {
		saleSkuCount := len(skusMapData)
		finalScore = int(math.Round(float64(saleSkuCount) * SaleSkuScorePerUnit))
		if finalScore > ItemTotalScore {
			finalScore = ItemTotalScore
		}
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldSaleSkuCount, finalScore)
}

//平均捡货时间小于等于拣货截止时间为10分满分，每超出1分钟，减1分
func ScoreAveragePickupTime(storeInfo *cms.StoreExt) {
	storeID := storeInfo.ID
	db := dao.GetDB()
	orderList, err := dao.GetDailyFinishOrderList(db, storeID, scoreDate)
	orderListCount := len(orderList)
	finalScore := 0
	if err == nil && orderListCount > 0 {
		totalScore := 0
		for _, value := range orderList {
			statusTime := value.StatusTime.Unix()
			pickDeadline := value.PickDeadline.Unix()
			if statusTime <= pickDeadline {
				totalScore += ItemTotalScore
			} else {
				decScore := int(math.Round(float64(statusTime-pickDeadline) / 60))
				tempScore := ItemTotalScore - decScore
				if tempScore < 0 {
					tempScore = 0
				}
				totalScore += tempScore
			}
		}
		finalScore = totalScore / orderListCount
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldAveragePickupTime, finalScore)
}

//差评订单和完成订单比例小于0.2%,得满分10分，每增加0.1%减1分
func ScoreBadCommentOrder(storeInfo *cms.StoreExt) {
	storeID := storeInfo.ID
	badCommentOrderCount := storeScoreDataWrapper.GetDailyBadCommentOrderCount(storeID)
	finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
	finalScore := 0
	if finishOrderCount > 0 {
		badCommentOrderRatio := float64(badCommentOrderCount) * 100 / float64(finishOrderCount)
		if badCommentOrderRatio <= BadCommentOrderNormalRatio {
			finalScore = ItemTotalScore
		} else {
			decScore := int(math.Round((badCommentOrderRatio - BadCommentOrderNormalRatio) / 0.1))
			finalScore = ItemTotalScore - decScore
			if finalScore < 0 {
				finalScore = 0
			}
		}
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldBadCommentOrder, finalScore)
}

//未完成订单和完成订单比小于1%，得满分10分，比例每增加5%，分数减1
func ScoreUnfinishOrder(storeInfo *cms.StoreExt) {
	storeID := storeInfo.ID
	unFinishOrderCount := storeScoreDataWrapper.GetDailyUnFinishOrderCount(storeID)
	finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
	finalScore := 0
	if finishOrderCount > 0 {
		unfinishOrderRatio := float64(unFinishOrderCount) * 100 / float64(finishOrderCount)
		if unfinishOrderRatio <= UnfinishOrderNormalRatio {
			finalScore = ItemTotalScore
		} else {
			decScore := int(math.Round((unfinishOrderRatio - UnfinishOrderNormalRatio) / 5))
			finalScore = ItemTotalScore - decScore
			if finalScore < 0 {
				finalScore = 0
			}
		}
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldUnfinishOrder, finalScore)
}

//缺货订单和完成订单比小于1%得10分，比例每增加0.1%减1分
func ScoreAbsentGoodsOrder(storeInfo *cms.StoreExt) {
	storeID := storeInfo.ID
	absentGoodsOrderCount := storeScoreDataWrapper.GetDailyAbsentGoodsOrderCount(storeID)
	finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
	finalScore := 0
	if finishOrderCount > 0 {
		absentGoodsOrderRatio := float64(absentGoodsOrderCount) * 100 / float64(finishOrderCount)
		if absentGoodsOrderRatio <= AbsentGoodsOrderNormalRatio {
			finalScore = ItemTotalScore
		} else {
			decScore := int(math.Round((absentGoodsOrderRatio - AbsentGoodsOrderNormalRatio) / 0.1))
			finalScore = ItemTotalScore - decScore
			if finalScore < 0 {
				finalScore = 0
			}
		}
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldAbsentGoodsOrder, finalScore)
}

//促销品数量20个以上为满分10分，每少2个扣1分
func ScorePromotionSku(storeInfo *cms.StoreExt) {
	storeID := storeInfo.ID
	db := dao.GetDB()
	beginTime := time.Now()
	endTime := time.Now()
	actStoreSkuList, err := dao.GetEffectiveActStoreSkuInfo(db, -1, nil, model.ActTypeAll, []int{storeID}, nil, beginTime, endTime)
	finalScore := 0
	if err == nil && len(actStoreSkuList) > 0 {
		actStoreSkuMap := make(map[int]int)
		for _, value := range actStoreSkuList {
			if value.Type != model.ActSkuFake {
				actStoreSkuMap[value.SkuID] = 1
			}
		}
		promotionSkuCount := len(actStoreSkuMap)
		if promotionSkuCount >= PromotionSkuNormalCount {
			finalScore = ItemTotalScore
		} else {
			decScore := (PromotionSkuNormalCount - promotionSkuCount) / 2
			finalScore = ItemTotalScore - decScore
			if finalScore < 0 {
				finalScore = 0
			}
		}
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldPromotionSku, finalScore)
}

//经营全平台满分10分，每少一个平台扣2分(一个平台没有得0分)
func ScoreFullVendor(storeInfo *cms.StoreExt) {
	fullVendorCount := len(fullVendorList)
	finalScore := 0
	storeID := storeInfo.ID
	storeStatus := storeInfo.Status
	isStoreOpen := storeStatus == model.StoreStatusOpened
	count := 0
	for _, storeMap := range storeInfo.StoreMaps {
		isSyncStoreSku := storeMap.IsSync
		vendorStoreStatus := storeMap.Status
		isVendorStoreOpen := vendorStoreStatus == model.StoreStatusOpened
		opTimeList := storeInfo.GetOpTimeList()
		if len(opTimeList) > 0 && isStoreOpen && isVendorStoreOpen && isSyncStoreSku != 0 {
			count++
		}
	}
	if count > 0 {
		if count == fullVendorCount {
			finalScore = ItemTotalScore
		} else {
			decScore := (fullVendorCount - count) * 2
			finalScore = ItemTotalScore - decScore
		}
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldFullVendor, finalScore)
}

//经营范围面积大于半径2km的圆得满分10分，低于1km得分0
func ScoreStoreRange(storeInfo *cms.StoreExt) {
	finalScore := 0
	storeID := storeInfo.ID
	if storeInfo.DeliveryRangeType == model.DeliveryRangeTypePolygon {
		if storeInfo.DeliveryRange != "" {
			points := jxutils.CoordinateStr2Points(storeInfo.DeliveryRange)
			area := jxutils.CalcPolygonAreaAutonavi(points)
			goodArea := math.Pi * StoreRangeGoodRadius * StoreRangeGoodRadius
			badArea := math.Pi * StoreRangeBadRadius * StoreRangeBadRadius
			if area >= goodArea {
				finalScore = ItemTotalScore
			} else if area <= badArea {
				finalScore = 0
			} else {
				diff := goodArea - area
				ratio := float64(ItemTotalScore) / (goodArea - badArea)
				finalScore = ItemTotalScore - int(math.Round(diff*ratio))
			}
		}
	} else if storeInfo.DeliveryRangeType == model.DeliveryRangeTypeRadius {
		deliveryRadius := utils.Str2Float64WithDefault(storeInfo.DeliveryRange, 0) / 1000
		if deliveryRadius >= StoreRangeGoodRadius {
			finalScore = ItemTotalScore
		} else if deliveryRadius <= StoreRangeBadRadius {
			finalScore = 0
		} else {
			diff := StoreRangeGoodRadius - deliveryRadius
			ratio := float64(ItemTotalScore) / (StoreRangeGoodRadius - StoreRangeBadRadius)
			finalScore = ItemTotalScore - int(math.Round(diff*ratio))
		}
	}
	if finalScore < 0 {
		finalScore = 0
	} else if finalScore > ItemTotalScore {
		finalScore = ItemTotalScore
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldStoreRange, finalScore)
}

//得到距离某个门店多少KM内的所有门店信息
func GetRangeStoreList(storeID int, lng, lat, checkRange float64, storeList []*cms.StoreExt) (outStoreList []*cms.StoreExt) {
	for _, storeInfo := range storeList {
		if storeInfo.ID == storeID {
			outStoreList = append(outStoreList, storeInfo)
		} else {
			distance := jxutils.EarthDistance(lng, lat, storeInfo.FloatLng, storeInfo.FloatLat)
			if distance <= checkRange {
				outStoreList = append(outStoreList, storeInfo)
			}
		}
	}

	return outStoreList
}

//得到给定门店列表里的同一SkuID商品的平均价格
func GetStoreSkusAveragePrice(storeList []*cms.StoreExt) map[int]int {
	skusTotalPrice := make(map[int]int)
	skusCount := make(map[int]int)
	skusAveragePrice := make(map[int]int)
	for _, storeInfo := range storeList {
		storeID := storeInfo.ID
		storeSkuMapData := allStoreSkusWrapper.GetData(storeID)
		for skuID, skuPrice := range storeSkuMapData {
			skusTotalPrice[skuID] += skuPrice
			skusCount[skuID]++
		}
	}
	for id, totalPrice := range skusTotalPrice {
		skusAveragePrice[id] = int(math.Round(float64(totalPrice) / float64(skusCount[id])))
	}
	return skusAveragePrice
}

func GetSkusCountLessEqualAvgPrice(storeID int, skusAveragePrice map[int]int) (count int) {
	storeSkuMapData := allStoreSkusWrapper.GetData(storeID)
	for skuID, skuPrice := range storeSkuMapData {
		skuAvgPrice := skusAveragePrice[skuID]
		if skuPrice <= skuAvgPrice {
			count++
		}
	}

	return count
}

//可售商品价格在附近5km内门店比较，价格低于等于平均值的商品数占90%以上满分10分，比例每降低10%减1分，100%超标得0分
func ScoreSaleSkuPrice(storeInfo *cms.StoreExt, storeList []*cms.StoreExt) {
	finalScore := 0
	storeID := storeInfo.ID
	totalCount := len(allStoreSkusWrapper.GetData(storeID))
	if totalCount > 0 {
		rangeStoreList := GetRangeStoreList(storeID, storeInfo.FloatLng, storeInfo.FloatLat, SaleSkuCheckRange, storeList)
		skusAveragePrice := GetStoreSkusAveragePrice(rangeStoreList)
		count := GetSkusCountLessEqualAvgPrice(storeID, skusAveragePrice)
		if count > 0 {
			ratio := int(math.Round(float64(count) * 100 / float64(totalCount)))
			if ratio >= SaleSkuPriceRatio {
				finalScore = ItemTotalScore
			} else {
				decScore := (SaleSkuPriceRatio - ratio) / 10
				finalScore = ItemTotalScore - decScore
				if finalScore < 0 {
					finalScore = 0
				}
			}
		}
	}
	storeScoreDataWrapper.SetData(storeID, model.FieldSaleSkuPrice, finalScore)
}

func GetFilterStoreListEx(storeList []*cms.StoreExt, storeIDMap map[int]int) (outStoreList []*cms.StoreExt) {
	for _, storeInfo := range storeList {
		storeID := storeInfo.ID
		if len(storeIDMap) > 0 {
			if _, ok := storeIDMap[storeID]; !ok {
				continue
			}
		}
		var tempStoreMaps []*model.StoreMap
		for _, vendorStoreInfo := range storeInfo.StoreMaps {
			vendorID := vendorStoreInfo.VendorID
			if _, ok := fullVendorList[vendorID]; !ok {
				continue
			}
			tempStoreMaps = append(tempStoreMaps, vendorStoreInfo)
		}
		if len(tempStoreMaps) > 0 {
			storeInfo.StoreMaps = tempStoreMaps
			outStoreList = append(outStoreList, storeInfo)
		}
	}

	return outStoreList
}

func ScoreStore(ctx *jxcontext.Context, storeIDList []int) (retVal interface{}, err error) {
	isScoring = true
	scoreDate = utils.GetCurDate()
	var storeList []*cms.StoreExt
	taskCount := 5
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			baseapi.SugarLogger.Debugf("ScoreStore step0 begin")
			storeScoreDataWrapper.InitData()
			storeList, err = GetStoreList(ctx)
			storeIDMap := jxutils.IntList2Map(storeIDList)
			storeList = GetFilterStoreListEx(storeList, storeIDMap)
			baseapi.SugarLogger.Debugf("ScoreStore step0 end")
		case 1:
			baseapi.SugarLogger.Debugf("ScoreStore step1 begin")
			GetAllStoreSkus(ctx, task, storeList)
			baseapi.SugarLogger.Debugf("ScoreStore step1 end")
		case 2:
			baseapi.SugarLogger.Debugf("ScoreStore step2 begin")
			db := dao.GetDB()
			storeCountList, err := dao.GetDailyBadCommentOrderCount(db, scoreDate)
			if err == nil {
				storeScoreDataWrapper.SetDailyBadCommentOrderCount(storeCountList)
			} else {
				baseapi.SugarLogger.Debugf("ScoreStore GetDailyBadCommentOrderCount %v", err)
			}
			storeCountList, err = dao.GetDailyUnFinishOrderCount(db, scoreDate)
			if err == nil {
				storeScoreDataWrapper.SetDailyUnFinishOrderCount(storeCountList)
			} else {
				baseapi.SugarLogger.Debugf("ScoreStore GetDailyUnFinishOrderCount %v", err)
			}
			storeCountList, err = dao.GetDailyFinishOrderCount(db, scoreDate)
			if err == nil {
				storeScoreDataWrapper.SetDailyFinishOrderCount(storeCountList)
			} else {
				baseapi.SugarLogger.Debugf("ScoreStore GetDailyFinishOrderCount %v", err)
			}
			storeCountList, err = dao.GetDailyAbsentGoodsOrderCount(db, scoreDate)
			if err == nil {
				storeScoreDataWrapper.SetDailyAbsentGoodsOrderCount(storeCountList)
			} else {
				baseapi.SugarLogger.Debugf("ScoreStore GetDailyAbsentGoodsOrderCount %v", err)
			}
			baseapi.SugarLogger.Debugf("ScoreStore step2 end")
		case 3:
			baseapi.SugarLogger.Debugf("ScoreStore step3 begin")
			taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				storeInfo := batchItemList[0].(*cms.StoreExt)
				storeID := storeInfo.ID
				baseapi.SugarLogger.Debugf("Begin store id:%d", storeID)
				ScoreStoreOpenTime(storeInfo)
				ScoreSaleSkuCount(storeInfo)
				ScoreAveragePickupTime(storeInfo)
				ScoreBadCommentOrder(storeInfo)
				ScoreUnfinishOrder(storeInfo)
				ScoreAbsentGoodsOrder(storeInfo)
				ScorePromotionSku(storeInfo)
				ScoreFullVendor(storeInfo)
				ScoreStoreRange(storeInfo)
				ScoreSaleSkuPrice(storeInfo, storeList)
				baseapi.SugarLogger.Debugf("End store id:%d", storeID)
				return retVal, err
			}
			taskParallel := tasksch.NewParallelTask("计算门店得分", tasksch.NewParallelConfig().SetParallelCount(ParallelCount), ctx, taskFunc, storeList)
			tasksch.HandleTask(taskParallel, task, true).Run()
			taskParallel.GetResult(0)
			_, err = taskParallel.GetResult(0)
			if err != nil {
				baseapi.SugarLogger.Debugf("ScoreStore taskParallel error:%v", err)
			}
			baseapi.SugarLogger.Debugf("ScoreStore step3 end")
		case 4:
			baseapi.SugarLogger.Debugf("ScoreStore step4 begin")
			storeScoreDataWrapper.InsertStoreScore()
			storeScoreDataWrapper.ClearData()
			allStoreSkusWrapper.ClearData()
			baseapi.SugarLogger.Debugf("ScoreStore step4 end")
			isScoring = false
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask("门店评分-序列任务", ctx, taskSeqFunc, taskCount)
	tasksch.HandleTask(taskSeq, nil, true).Run()

	return retVal, err
}

func ScheduleScoreStore() {
	if EnableScheduleScoreStore {
		ScheduleTimerFunc("ScheduleScoreStore", func() {
			if !isScoring {
				ScoreStore(jxcontext.AdminCtx, nil)
			}
		}, scoreStoreTimeList)
		CheckScoreStore()
	}
}

func CheckScoreStore() {
	if !isScoring {
		curTime := time.Now()
		checkTimeStr1 := fmt.Sprintf("%s %s", utils.Time2DateStr(curTime), scoreStoreTimeList[0])
		checkTime1 := utils.Str2Time(checkTimeStr1)
		checkTimeStr2 := fmt.Sprintf("%s %s", utils.Time2DateStr(curTime), scoreStoreCheckTimeEnd)
		checkTime2 := utils.Str2Time(checkTimeStr2)
		if curTime.Unix() >= checkTime1.Unix() && curTime.Unix() <= checkTime2.Unix() {
			db := dao.GetDB()
			hasStoreScoreData, err := dao.CheckHasStoreScoreData(db, curTime)
			if err == nil && !hasStoreScoreData {
				ScoreStore(jxcontext.AdminCtx, nil)
			}
		}
	}
}

func Time2Week(t time.Time) int {
	yearDay := t.YearDay()
	yearFirstDay := t.AddDate(0, 0, -yearDay+1)
	firstDayInWeek := int(yearFirstDay.Weekday())

	firstWeekDays := 1
	if firstDayInWeek != 0 {
		firstWeekDays = 7 - firstDayInWeek + 1
	}
	var week int
	if yearDay <= firstWeekDays {
		week = 1
	} else {
		tempWeek := (float64(yearDay) - float64(firstWeekDays)) / float64(7)
		week = int(math.Ceil(tempWeek)) + 1
	}

	return week
}

func SplitToSingleWeekDataList(storeScoreList []*model.StoreScoreEx) (weekDataList [][]*model.StoreScoreEx) {
	singleWeekData := []*model.StoreScoreEx{}
	weekIndex := 0
	for _, value := range storeScoreList {
		if weekIndex == 0 {
			weekIndex = Time2Week(value.ScoreDate)
		}
		if weekIndex == Time2Week(value.ScoreDate) {
			singleWeekData = append(singleWeekData, value)
		} else {
			weekDataList = append(weekDataList, singleWeekData)
			singleWeekData = []*model.StoreScoreEx{}
			weekIndex = 0
			singleWeekData = append(singleWeekData, value)
		}
	}
	if len(singleWeekData) > 0 {
		weekDataList = append(weekDataList, singleWeekData)
	}

	return weekDataList
}

func GetStoreScoreLevel(score int) int {
	level := 0
	if score >= GoldMedalScore {
		level = GoldMedalLevel
	} else if score >= SilverMedalScore {
		level = SilverMedalLevel
	} else if score >= BronzeMedalScore {
		level = BronzeMedalLevel
	}

	return level
}

func GetWeeklyStoreScore(storeID, weekIndexParam int) (outWeeklyStoreScoreDataList []*model.WeeklyStoreScore, err error) {
	db := dao.GetDB()
	storeScoreList, err := dao.GetWeeklyStoreScoreList(db, storeID, WeekNum)
	if err == nil && len(storeScoreList) > 0 {
		weeklyStoreScoreDataList := []*model.WeeklyStoreScore{}
		weekDataList := SplitToSingleWeekDataList(storeScoreList)
		for weekIndex, weekData := range weekDataList {
			weeklyData := &model.WeeklyStoreScore{}
			weeklyData.ID = weekIndex
			weeklyData.ItemTotalScore = ItemTotalScore
			weeklyData.StoreID = storeID
			weeklyStoreScoreDataList = append(weeklyStoreScoreDataList, weeklyData)
			weekDataCount := len(weekData)
			for dayIndex, dayData := range weekData {
				for _, fieldName := range storeScoreFieldName {
					srcFieldValue := refutil.GetObjFieldByName(dayData, fieldName).(int)
					destFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
					refutil.SetObjFieldByName(weeklyData, fieldName, destFieldValue+srcFieldValue)
				}
				if weekDataCount == 1 {
					weeklyData.BeginTime = dayData.ScoreDate
					weeklyData.EndTime = dayData.ScoreDate
				} else {
					if dayIndex == 0 {
						weeklyData.EndTime = dayData.ScoreDate
					} else if dayIndex == weekDataCount-1 {
						weeklyData.BeginTime = dayData.ScoreDate
					}
				}
				weeklyData.StoreName = dayData.StoreName
			}
			for _, fieldName := range storeScoreFieldName {
				destFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
				refutil.SetObjFieldByName(weeklyData, fieldName, int(math.Round(float64(destFieldValue)/float64(weekDataCount))))
			}
			for _, fieldName := range storeScoreFieldName {
				srcFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
				destFieldValue := refutil.GetObjFieldByName(weeklyData, model.FieldTotalScore).(int)
				refutil.SetObjFieldByName(weeklyData, model.FieldTotalScore, destFieldValue+srcFieldValue)
			}
			weeklyData.Level = GetStoreScoreLevel(weeklyData.TotalScore)
		}
		if weekIndexParam == -1 {
			outWeeklyStoreScoreDataList = weeklyStoreScoreDataList
		} else {
			fmt.Println("testss", utils.Format4Output(weeklyStoreScoreDataList, false))
			outWeeklyStoreScoreDataList = []*model.WeeklyStoreScore{weeklyStoreScoreDataList[weekIndexParam]}
		}
	}

	return outWeeklyStoreScoreDataList, err
}

func GetStoreTotalScoreList(ctx *jxcontext.Context, storeIDList []int, cityCode int, keyWord string, beginTime, endTime time.Time, isDesc bool, checkScoreLow, checkScoreHigh, offset, pageSize int) (storeTotalScoreEx model.StoreTotalScoreEx, err error) {
	db := dao.GetDB()
	//权限
	if permission.IsRoled(ctx) {
		if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
			var storeIDs2 []int
			if len(storeIDList) > 0 {
				for _, v := range storeIDList {
					if storeIDsMap[v] != 0 {
						storeIDs2 = append(storeIDs2, v)
					}
				}
				if len(storeIDs2) == 0 {
					storeIDs2 = append(storeIDs2, -1)
				}
			} else {
				for k, _ := range storeIDsMap {
					storeIDs2 = append(storeIDs2, k)
				}
			}
			storeIDList = nil
			storeIDList = storeIDs2
		}
	}
	storeTotalScoreMapData := make(map[int]*model.StoreTotalScore)
	storeTotalScoreList, err := dao.GetStoreTotalScoreList(db, storeIDList, cityCode, keyWord, beginTime, endTime)
	var filterStoreTotalScoreList []*model.StoreTotalScore
	if err == nil && len(storeTotalScoreList) > 0 {
		countDayNum := make(map[int]int)
		for _, value := range storeTotalScoreList {
			storeID := value.StoreID
			if storeTotalScoreMapData[storeID] == nil {
				storeTotalScore := &model.StoreTotalScore{}
				storeTotalScore.StoreID = value.StoreID
				storeTotalScore.StoreName = value.StoreName
				storeTotalScore.CityName = value.CityName
				storeTotalScoreMapData[storeID] = storeTotalScore
			}
			storeTotalScore := storeTotalScoreMapData[storeID]
			storeTotalScore.StoreScore += value.StoreScore
			countDayNum[storeID]++
		}
		for storeID, value := range storeTotalScoreMapData {
			value.StoreScore = int(math.Round(float64(value.StoreScore) / float64(countDayNum[storeID])))
			needAdd := true
			if checkScoreLow > 0 && value.StoreScore < checkScoreLow {
				needAdd = false
			}
			if checkScoreHigh > 0 && value.StoreScore > checkScoreHigh {
				needAdd = false
			}
			if needAdd {
				filterStoreTotalScoreList = append(filterStoreTotalScoreList, value)
			}
		}
		if isDesc {
			sort.Slice(filterStoreTotalScoreList, func(i, j int) bool {
				data1 := filterStoreTotalScoreList[i]
				data2 := filterStoreTotalScoreList[j]
				if data1.StoreScore == data2.StoreScore {
					return data1.StoreID < data2.StoreID
				} else {
					return data1.StoreScore > data2.StoreScore
				}
			})
		} else {
			sort.Slice(filterStoreTotalScoreList, func(i, j int) bool {
				data1 := filterStoreTotalScoreList[i]
				data2 := filterStoreTotalScoreList[j]
				if data1.StoreScore == data2.StoreScore {
					return data1.StoreID < data2.StoreID
				} else {
					return data1.StoreScore < data2.StoreScore
				}
			})
		}
	}

	offset = jxutils.FormalizePageOffset(offset)
	pageSize = jxutils.FormalizePageSize(pageSize)
	var pagedStoreTotalScoreList []*model.StoreTotalScore
	for i := offset; i < offset+pageSize && i < len(filterStoreTotalScoreList); i++ {
		pagedStoreTotalScoreList = append(pagedStoreTotalScoreList, filterStoreTotalScoreList[i])
	}

	storeTotalScoreEx.TotalCount = len(filterStoreTotalScoreList)
	storeTotalScoreEx.StoreTotalScoreList = pagedStoreTotalScoreList

	return storeTotalScoreEx, err
}
