package jd

import (
	"fmt"
	"time"

	"git.rosy.net.cn/jx-callback/business/model/dao"
	"git.rosy.net.cn/jx-callback/business/partner"

	"git.rosy.net.cn/jx-callback/business/jxutils"

	"git.rosy.net.cn/jx-callback/globals"

	"git.rosy.net.cn/baseapi/platformapi/jdapi"
	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/baseapi/utils/errlist"
	"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"
)

const (
	actMapDuration = 2 * time.Hour
)

type LogicUpdateInfo struct {
	Item      interface{}
	KVs       map[string]interface{}
	Condition map[string]interface{}
}

var (
	jdSkuActStatusMap = map[int]int{
		jdapi.PromotionStateNotConfirm: model.ActStatusNA,
		jdapi.PromotionStateConfirmed:  model.ActStatusCreated,
		jdapi.PromotionStateCanceled:   model.ActStatusCanceled,
		jdapi.PromotionStateEnded:      model.ActStatusEnded,
	}

	actMap jxutils.SyncMapWithTimeout
)

// 是否按单一门店商品维度创建活动
func isCreateTypeSingleStoreSku() bool {
	return false
	// return !globals.IsProductEnv()
}

func splitPromotionSku(skus []*jdapi.PromotionSku, maxCount int) (skusList [][]*jdapi.PromotionSku) {
	for {
		skusLen := len(skus)
		if skusLen <= maxCount {
			skusList = append(skusList, skus)
			break
		}
		skusList = append(skusList, skus[:maxCount])
		skus = skus[maxCount:]
	}
	return skusList
}

func jdSkuActType2Jx(actType int) int {
	if actType == jdapi.PromotionTypeDirectDown {
		return model.ActSkuDirectDown
	} else if actType == jdapi.PromotionTypeSeckill || actType == jdapi.PromotionTypeLimitedTime {
		return model.ActSkuSecKill
	}
	return 0
}

func jdSkuActStatus2Jx(jdActState int) int {
	return jdSkuActStatusMap[jdActState]
}

func CreatePromotionInfos(vendorOrgCode string, promotionType int, name string, beginDate, endDate time.Time, outInfoId, advertising, traceId string) (infoId int64, err error) {
	if globals.EnableJdStoreWrite {
		if promotionType == model.ActSkuDirectDown {
			infoId, err = getAPI(vendorOrgCode).CreatePromotionInfosSingle(name, beginDate, endDate, outInfoId, advertising, traceId)
		} else {
			infoId, err = getAPI(vendorOrgCode).CreatePromotionInfosLimitTime(name, beginDate, endDate, outInfoId, advertising, traceId)
		}
	} else {
		infoId = jxutils.GenFakeID()
	}
	if err == nil {
		actMap.StoreWithTimeout(infoId, 1, actMapDuration)
	}
	return infoId, err
}

func CreatePromotionRules(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int, traceId string) (err error) {
	if globals.EnableJdStoreWrite {
		if promotionType == model.ActSkuDirectDown {
			err = getAPI(vendorOrgCode).CreatePromotionRulesSingle(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId)
		} else {
			err = getAPI(vendorOrgCode).CreatePromotionRulesLimitTime(infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId)
		}
	}
	return err
}

func CreatePromotionSku(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (skusResult []*jdapi.PromotionSku, err error) {
	if globals.EnableJdStoreWrite {
		for _, batchSkus := range splitPromotionSku(skus, jdapi.MaxPromotionSkuCount) {
			var tmpSkusResult []*jdapi.PromotionSku
			var tmpErr error
			if promotionType == model.ActSkuDirectDown {
				tmpSkusResult, tmpErr = getAPI(vendorOrgCode).CreatePromotionSkuSingle(infoId, outInfoId, batchSkus, traceId)
			} else {
				tmpSkusResult, tmpErr = getAPI(vendorOrgCode).CreatePromotionSkuLimitTime(infoId, outInfoId, batchSkus, traceId)
			}
			if err = tmpErr; err != nil {
				break
			}
			skusResult = append(skusResult, tmpSkusResult...)
		}
	}
	return skusResult, err
}

func CancelPromotionSku(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (err error) {
	if globals.EnableJdStoreWrite {
		for _, batchSkus := range splitPromotionSku(skus, jdapi.MaxPromotionSkuCount) {
			var tmpErr error
			if promotionType == model.ActSkuDirectDown {
				tmpErr = getAPI(vendorOrgCode).CancelPromotionSkuSingle(infoId, outInfoId, batchSkus, traceId)
			} else {
				tmpErr = getAPI(vendorOrgCode).CancelPromotionSkuLimitTime(infoId, outInfoId, batchSkus, traceId)
			}
			if err = tmpErr; err != nil {
				break
			}
		}
	}
	return err
}

func ConfirmPromotion(vendorOrgCode string, promotionType int, infoId int64, outInfoId, traceId string) (err error) {
	if globals.EnableJdStoreWrite {
		if promotionType == model.ActSkuDirectDown {
			return getAPI(vendorOrgCode).ConfirmPromotionSingle(infoId, outInfoId, traceId)
		} else {
			return getAPI(vendorOrgCode).ConfirmPromotionLimitTime(infoId, outInfoId, traceId)
		}
	}
	return err
}

func CancelPromotion(vendorOrgCode string, promotionType int, infoId int64, outInfoId, traceId string) (err error) {
	if globals.EnableJdStoreWrite {
		if promotionType == model.ActSkuDirectDown {
			err = getAPI(vendorOrgCode).CancelPromotionSingle(infoId, outInfoId, traceId)
		} else {
			err = getAPI(vendorOrgCode).CancelPromotionLimitTime(infoId, outInfoId, traceId)
		}
	}
	return err
}

func AdjustPromotionTime(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, endDate time.Time, traceId string) (err error) {
	if globals.EnableJdStoreWrite {
		if promotionType == model.ActSkuDirectDown {
			err = getAPI(vendorOrgCode).AdjustPromotionTimeSingle(infoId, outInfoId, endDate, traceId)
		} else {
			err = getAPI(vendorOrgCode).AdjustPromotionTimeLimitTime(infoId, outInfoId, endDate, traceId)
		}
	}
	return err
}

func AdjustPromotionSku(vendorOrgCode string, promotionType int, infoId int64, outInfoId string, skus []*jdapi.PromotionSku, traceId string) (skusResult []*jdapi.PromotionSku, err error) {
	if globals.EnableJdStoreWrite {
		if promotionType == model.ActSkuDirectDown {
			skusResult, err = getAPI(vendorOrgCode).AdjustPromotionSkuSingle(infoId, outInfoId, skus, traceId)
		} else {
			skusResult, err = getAPI(vendorOrgCode).AdjustPromotionSkuLimitTime(infoId, outInfoId, skus, traceId)
		}
	}
	return skusResult, err
}

func storeSku2Jd(actStoreSku []*model.ActStoreSku2, handler func(syncStatus int8) bool) (jdActStoreSku []*jdapi.PromotionSku) {
	for _, v := range actStoreSku {
		if handler(v.SyncStatus) {
			if v.VendorStoreID != "" && v.VendorSkuID != "" {
				jdActStoreSku = append(jdActStoreSku, &jdapi.PromotionSku{
					StationNo:      utils.Str2Int64(v.VendorStoreID),
					SkuID:          utils.Str2Int64(v.VendorSkuID),
					PromotionPrice: v.ActualActPrice,
					LimitSkuCount:  v.Stock,
				})
			}
		}
	}
	return jdActStoreSku
}

func createSkuAct(ctx *jxcontext.Context, act *model.Act2, actStoreSku []*model.ActStoreSku2) (vendorActID string, err error) {
	traceInfo := ctx.GetTrackInfo()
	outInfoID := ""
	if act.VendorActID == "" {
		outInfoID = utils.Int2Str(act.ID)
	}
	infoID, err2 := CreatePromotionInfos(act.VendorOrgCode, act.Type, act.GetRealActName(), act.BeginAt, act.EndAt, outInfoID, act.Advertising, traceInfo)
	if err = err2; err == nil {
		vendorActID = utils.Int64ToStr(infoID)
		if err = CreatePromotionRules(act.VendorOrgCode, act.Type, infoID, "", act.LimitUser, act.LimitUser, act.LimitCount, 1, traceInfo); err == nil {
			if _, err = CreatePromotionSku(act.VendorOrgCode, act.Type, infoID, utils.Int2Str(act.ID), storeSku2Jd(actStoreSku, model.IsSyncStatusNeedCreate), traceInfo); err == nil {
				if err = ConfirmPromotion(act.VendorOrgCode, act.Type, infoID, "", traceInfo); err == nil {
					for _, v := range actStoreSku {
						v.VendorActID = vendorActID
					}
				}
			}
		}
		if err != nil {
			CancelPromotion(act.VendorOrgCode, act.Type, infoID, "", traceInfo)
		}
	}
	return vendorActID, err
}

func proxyCreateSkuAct(ctx *jxcontext.Context, act *model.Act2, actStoreSku []*model.ActStoreSku2) (vendorActID string, err error) {
	if isCreateTypeSingleStoreSku() && len(actStoreSku) > 1 {
		errList := errlist.New()
		vendorActID := act.VendorActID
		act.VendorActID = "placeholder"
		for _, v := range actStoreSku {
			_, err := createSkuAct(ctx, act, []*model.ActStoreSku2{v})
			errList.AddErr(err)
		}
		act.VendorActID = vendorActID
		err = errList.GetErrListAsOne()
	} else {
		vendorActID, err = createSkuAct(ctx, act, actStoreSku)
	}
	return vendorActID, err
}

func cancelSkuActSkus(ctx *jxcontext.Context, act *model.Act2, vendorActID string, actStoreSku []*model.ActStoreSku2) (err error) {
	if vendorActID != "" {
		if skuList := storeSku2Jd(actStoreSku, model.IsSyncStatusNeedDelete); len(skuList) > 0 {
			err = CancelPromotionSku(act.VendorOrgCode, act.Type, utils.Str2Int64(vendorActID), "", skuList, ctx.GetTrackInfo())
		}
	}
	return err
}

func cancelSkuAct(ctx *jxcontext.Context, act *model.Act2, vendorActID string) (err error) {
	if vendorActID != "" {
		err = CancelPromotion(act.VendorOrgCode, act.Type, utils.Str2Int64(vendorActID), "", ctx.GetTrackInfo())
	}
	return err
}

func (c *PurchaseHandler) SyncAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreSkuList []*model.ActStoreSku2) (err error) {
	vendorActInfoMap := make(map[string][]*model.ActStoreSku2)
	deleteActInfoMap := make(map[string][]*model.ActStoreSku2)
	var actStoreSkuList4Create []*model.ActStoreSku2
	var updateItems []*dao.KVUpdateItem

	actStoreSkuMap := partner.SplitActStoreSku(actStoreSkuList)
	actSkuCount := 0
	toDelActSkuCount := 0
	for storeID := range actStoreSkuMap {
		for _, actStoreSku := range actStoreSkuMap[storeID] {
			vendorActID := actStoreSku.VendorActID
			if vendorActID == "" {
				vendorActID = act.VendorActID
			}
			actSkuCount++
			vendorActInfoMap[vendorActID] = append(vendorActInfoMap[vendorActID], actStoreSku)
			if model.IsSyncStatusDelete(actStoreSku.SyncStatus) {
				toDelActSkuCount++
				deleteActInfoMap[vendorActID] = append(deleteActInfoMap[vendorActID], actStoreSku)
			} else if model.IsSyncStatusNew(actStoreSku.SyncStatus) {
				actStoreSkuList4Create = append(actStoreSkuList4Create, actStoreSku)
			}
		}
	}
	// 如果是全删，直接添加删除（即取消）标志
	if actSkuCount == toDelActSkuCount {
		act.SyncStatus |= model.SyncFlagDeletedMask
	}
	db := dao.GetDB()
	err = func() (err error) {
		if model.IsSyncStatusDelete(act.SyncStatus) {
			errList := errlist.New()
			for vendorActID := range vendorActInfoMap {
				if vendorActID != "" {
					if err = cancelSkuAct(ctx, act, vendorActID); err == nil {
						updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, vendorActInfoMap[vendorActID], model.SyncFlagModifiedMask)...)
					} else {
						errList.AddErr(err)
					}
				}
			}
			if err = errList.GetErrListAsOne(); err == nil {
				updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagModifiedMask))
			}
		} else if model.IsSyncStatusNew(act.SyncStatus) {
			if act.VendorActID, err = proxyCreateSkuAct(ctx, act, actStoreSkuList4Create); err == nil {
				updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, actStoreSkuList4Create, model.SyncFlagNewMask)...)
				updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagNewMask))
			} else {
				if act.VendorActID != "" {
					actMap := partner.Act2ActMap(act)
					dao.UpdateEntity(db, actMap, model.FieldVendorActID)
				}
			}
		} else if model.IsSyncStatusUpdate(act.SyncStatus) {
			errList := errlist.New()
			if len(actStoreSkuList4Create) > 0 {
				if _, err = proxyCreateSkuAct(ctx, act, actStoreSkuList4Create); err == nil {
					updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, actStoreSkuList4Create, model.SyncFlagNewMask)...)
				} else {
					errList.AddErr(err)
				}
			}
			for vendorActID := range deleteActInfoMap {
				if vendorActID != "" {
					if len(vendorActInfoMap[vendorActID]) == len(deleteActInfoMap[vendorActID]) {
						err = cancelSkuAct(ctx, act, vendorActID)
					} else {
						err = cancelSkuActSkus(ctx, act, vendorActID, deleteActInfoMap[vendorActID])
					}
					if err == nil {
						updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, deleteActInfoMap[vendorActID], model.SyncFlagDeletedMask)...)
					} else {
						errList.AddErr(err)
					}
				} else {
					updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, deleteActInfoMap[vendorActID], model.SyncFlagDeletedMask)...)
				}
			}
			if err = errList.GetErrListAsOne(); err == nil {
				updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagModifiedMask))
			}
		}
		return err
	}()
	_, err2 := dao.BatchUpdateActEntity(db, model.IsSyncStatusDelete(act.SyncStatus), updateItems)
	if err == nil {
		err = err2
	}
	return err
}

func OnActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
	jxutils.CallMsgHandler(func() {
		retVal = CurPurchaseHandler.onActMsg(msg)
	}, jxutils.ComposeUniversalOrderID(msg.BillID, model.VendorIDJD))
	return retVal
}

func (c *PurchaseHandler) onActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) {
	if msg.StatusID == jdapi.PromotionStatusSingleOK || msg.StatusID == jdapi.PromotionStatusLimitTimeOK {
		promotionID := msg.BillID
		intPromotionID := utils.Str2Int64(promotionID)
		if _, ok := actMap.Load(intPromotionID); !ok {
			utils.CallFuncAsync(func() {
				if !partner.CurActManager.IsVendorActExist(jxcontext.AdminCtx, promotionID, model.VendorIDJD) {
					act, actStoreSkuList, err := getActFromJD(AppKey2OrgCode(msg.AppKey), promotionID)
					if err == nil && len(actStoreSkuList) > 0 {
						_, err = partner.CurActManager.CreateActFromVendor(jxcontext.AdminCtx, act, actStoreSkuList)
					}
					if err != nil {
						retVal = jdapi.Err2CallbackResponse(err, promotionID)
					}
				}
			})
		} else {
			actMap.Delete(intPromotionID)
		}
	}
	return retVal
}

func getActFromJD(vendorOrgCode, promotionID string) (act *model.Act2, actStoreSkuList []*model.ActStoreSku2, err error) {
	result, err := getAPI(vendorOrgCode).QueryPromotionInfo(utils.Str2Int64(promotionID))
	if err == nil && len(result.SkuResultList) > 0 {
		act = &model.Act2{
			Act: model.Act{
				Name:            fmt.Sprintf("%s-%d", result.Source, result.PromotionInfoID),
				Type:            jdSkuActType2Jx(result.PromotionType),
				Status:          jdSkuActStatus2Jx(result.PromotionState),
				BeginAt:         result.SkuResultList[0].BeginTime.GoTime(),
				EndAt:           result.SkuResultList[0].EndTime.GoTime(),
				Source:          result.Source,
				CreateType:      model.ActCreateTypeCallback,
				PricePercentage: 0,
				LimitDaily:      result.SkuResultList[0].LimitDaily,
				LimitCount:      1,
			},
			VendorID:      model.VendorIDJD,
			VendorOrgCode: vendorOrgCode,
			VendorActID:   promotionID,
		}
		if utils.IsTimeZero(act.BeginAt) {
			act.BeginAt = result.BeginTime.GoTime()
		}
		if utils.IsTimeZero(act.EndAt) {
			act.EndAt = result.EndTime.GoTime().Add(24*time.Hour - 1*time.Second)
		}
		if result.SkuResultList[0].LimitPin == 1 || result.SkuResultList[0].LimitDevice == 1 {
			act.LimitUser = 1
		}

		for _, v := range result.SkuResultList {
			if v.PromotionState != jdapi.PromotionStateCanceled {
				actStoreSkuList = append(actStoreSkuList, &model.ActStoreSku2{
					VendorStoreID:  utils.Int64ToStr(v.StationNo),
					VendorSkuID:    utils.Int64ToStr(v.SkuID),
					ActualActPrice: int64(v.PromotionPrice),
				})
			}
		}
	}
	return act, actStoreSkuList, err
}

func (c *PurchaseHandler) GetActAmple(ctx *jxcontext.Context, vendorStoreID, vendorOrgCode string) (ample int, err error) {
	return ample, err
}
