package com.liquidnet.service.goblin.service.impl.manage;

import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.base.PagedResult;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.dto.GoblinStoreMarketDto;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtCouponActionParam;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtCouponFilterParam;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtCouponInfoVo;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtCouponListVo;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinStoreMgtCouponSpuListVo;
import com.liquidnet.service.goblin.dto.vo.GoblinGoodsInfoVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSelfGoodsCategoryVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreCouponBasicVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreCouponVo;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreMgtExtraService;
import com.liquidnet.service.goblin.service.manage.IGoblinstoreMgtCouponService;
import com.liquidnet.service.goblin.util.GoblinMongoUtils;
import com.liquidnet.service.goblin.util.GoblinRedisUtils;
import com.liquidnet.service.goblin.util.ObjectUtil;
import com.liquidnet.service.goblin.util.QueueUtils;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GoblinStoreMgtCouponServiceImpl implements IGoblinstoreMgtCouponService {
    @Autowired
    QueueUtils queueUtils;
    @Autowired
    GoblinRedisUtils goblinRedisUtils;
    @Autowired
    GoblinMongoUtils goblinMongoUtils;
    @Autowired
    IGoblinStoreMgtExtraService goblinStoreMgtExtraService;

    @Override
    public PagedResult<GoblinStoreMgtCouponListVo> couponList(GoblinStoreMgtCouponFilterParam filterParam) {
        PagedResult<GoblinStoreMgtCouponListVo> mgtCouponListVoPagedResult = goblinMongoUtils.getMgtStoreCouponListVos(filterParam);
        if (mgtCouponListVoPagedResult.getTotal() > 0) {
            List<GoblinStoreMgtCouponListVo> volist = mgtCouponListVoPagedResult.getList();
            List<String> storeCouponIdList = volist.stream().map(GoblinStoreMgtCouponListVo::getStoreCouponId).collect(Collectors.toList());

            List<Document> aggregateUserCouponResults = goblinMongoUtils.aggregateMgtUserCoupon(storeCouponIdList);
            if (!CollectionUtils.isEmpty(aggregateUserCouponResults)) {
                volist.forEach(vo -> {
                    List<Document> aggregateUserCouponResult = aggregateUserCouponResults.stream()
                            .filter(r -> r.getString("storeCouponId").equals(vo.getStoreCouponId())).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(aggregateUserCouponResult)) {
                        int receiveNum = 0, usedNum = 0;
                        for (Document aggregateDoc : aggregateUserCouponResult) {
                            Integer totalCount = aggregateDoc.getInteger("totalCount");
                            switch (aggregateDoc.getInteger("state")) {// 用户券状态[1-可用|2-无效|3-已过期|5-已使用]
                                case 5:
                                    usedNum += totalCount;
                                default:
                                    receiveNum += totalCount;
                            }
                        }
                        vo.setReceiveNum(receiveNum);
                        vo.setUsedNum(usedNum);
                    }
                });
            } else {

            }
        }
        return mgtCouponListVoPagedResult;
    }

    @Override
    public void couponActivityProcessing(GoblinStoreMgtCouponActionParam mgtCouponActionParam, String uid) {
        LocalDateTime now = LocalDateTime.now();
        String storeId = mgtCouponActionParam.getStoreId();
        String state = mgtCouponActionParam.getAction().equals("DISABLED") ? "3" : "0";
        List<String> storeCouponIdList = mgtCouponActionParam.getStoreCouponIdList();

        if (goblinMongoUtils.activityMgtStoreCouponBasicVo(uid, now, state, storeCouponIdList)) {
            storeCouponIdList.forEach(storeCouponId -> goblinRedisUtils.del(GoblinRedisConst.STORE_COUPON.concat(storeCouponId)));// 删除REDIS缓存

            List<GoblinStoreMarketDto> storeMarketDtos = goblinRedisUtils.getStoreMarketDtos(storeId);
            if (!CollectionUtils.isEmpty(storeMarketDtos)) {
                int beforeSize = storeMarketDtos.size();
                storeMarketDtos.removeIf(dto -> (dto.getType() == 1 && storeCouponIdList.contains(dto.getId())));
                if (beforeSize > storeMarketDtos.size()) {
                    goblinRedisUtils.setStoreMarketDtos(storeId, storeMarketDtos);
                }
            }

            LinkedList<Object[]> activityStoreCouponObjs = CollectionUtil.linkedListObjectArr();
            storeCouponIdList.forEach(storeCouponId -> activityStoreCouponObjs.add(new Object[]{state, uid, now, storeCouponId}));

            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_COUPON.getKey(),
                    SqlMapping.get("goblin_store_coupon.activity", activityStoreCouponObjs));
        }
    }

    @Override
    public void couponRemove(GoblinStoreMgtCouponActionParam mgtCouponActionParam, String uid) {
        LocalDateTime now = LocalDateTime.now();
        String storeId = mgtCouponActionParam.getStoreId();
        List<String> storeCouponIdList = mgtCouponActionParam.getStoreCouponIdList();

        if (goblinMongoUtils.delMgtStoreCouponBasicVos(storeCouponIdList, uid, now)) {
            storeCouponIdList.forEach(storeCouponId -> goblinRedisUtils.del(GoblinRedisConst.STORE_COUPON.concat(storeCouponId)));// 删除REDIS缓存

            List<GoblinStoreMarketDto> storeMarketDtos = goblinRedisUtils.getStoreMarketDtos(storeId);
            if (!CollectionUtils.isEmpty(storeMarketDtos)) {
                int beforeSize = storeMarketDtos.size();
                storeMarketDtos.removeIf(dto -> (dto.getType() == 1 && storeCouponIdList.contains(dto.getId())));
                if (beforeSize > storeMarketDtos.size()) {
                    goblinRedisUtils.setStoreMarketDtos(storeId, storeMarketDtos);
                }
            }

            LinkedList<Object[]> deleteStoreCouponObjs = CollectionUtil.linkedListObjectArr();
            storeCouponIdList.forEach(storeCouponId -> deleteStoreCouponObjs.add(new Object[]{uid, now, storeCouponId}));

            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_COUPON.getKey(),
                    SqlMapping.get("goblin_store_coupon.delete", deleteStoreCouponObjs));
        }
    }

    @Override
    public void couponAdd(String uid, GoblinStoreCouponBasicVo storeCouponBasicVo) {
        String storeCouponId = IDGenerator.nextMilliId();
        LocalDateTime now = LocalDateTime.now();

        boolean activityStartFlg = storeCouponBasicVo.getStartTime().isBefore(now);

        storeCouponBasicVo.setState(activityStartFlg ? "1" : "0");
        storeCouponBasicVo.setStoreCouponId(storeCouponId);
        storeCouponBasicVo.setStoreCouponNo(storeCouponId);
        storeCouponBasicVo.setDelFlg("0");
        storeCouponBasicVo.setCreatedAt(now);
        storeCouponBasicVo.setCreatedBy(uid);

        goblinMongoUtils.setMgtStoreCouponBasicVo(storeCouponBasicVo);
        Integer stock = storeCouponBasicVo.getStock();
        if (!stock.equals(0)) {
            goblinRedisUtils.setStoreCouponStock(storeCouponId, stock);
        }

        if (activityStartFlg) {// 活动已开始，处理店铺活动数据
            GoblinStoreMarketDto storeMarketDto = GoblinStoreMarketDto.getNew();
            storeMarketDto.setId(storeCouponId);
            storeMarketDto.setType(1);

            goblinRedisUtils.addStoreMarketDto(storeCouponBasicVo.getStoreId(), storeMarketDto);
        }

        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        toMqSqls.add(SqlMapping.get("goblin_store_coupon.insert"));
        LinkedList<Object[]> initStoreCouponObjs = CollectionUtil.linkedListObjectArr();
        initStoreCouponObjs.add(new Object[]{
                storeCouponId, storeCouponBasicVo.getStoreCouponNo(), storeCouponBasicVo.getStoreId(),
                storeCouponBasicVo.getTitle(), storeCouponBasicVo.getLabel(), storeCouponBasicVo.getNotice(),
                storeCouponBasicVo.getType(), stock, storeCouponBasicVo.getTriggers(),
                storeCouponBasicVo.getValFace(), storeCouponBasicVo.getDiscount(), storeCouponBasicVo.getValOver(),
                storeCouponBasicVo.getValMinus(), storeCouponBasicVo.getDeduction(), storeCouponBasicVo.getReceiveLimit(),
                storeCouponBasicVo.getReceiveCurb(), storeCouponBasicVo.getUseScope(), storeCouponBasicVo.getState(),
                storeCouponBasicVo.getStartTime(), storeCouponBasicVo.getEndTime(), storeCouponBasicVo.getCreatedBy(),
                storeCouponBasicVo.getCreatedAt()
        });
        toMqSqls.add(SqlMapping.get("goblin_store_coupon_rule.insert"));
        LinkedList<Object[]> initStoreCouponRuleObjs = CollectionUtil.linkedListObjectArr();
        if ("1".equals(storeCouponBasicVo.getUseScope()) && !CollectionUtils.isEmpty(storeCouponBasicVo.getSpuIdList())) {// 部分商品
            storeCouponBasicVo.getSpuIdList().forEach(spuId -> initStoreCouponRuleObjs.add(new Object[]{storeCouponId, spuId, uid, now}));
        }

        queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_COUPON.getKey(),
                SqlMapping.gets(toMqSqls, initStoreCouponObjs, initStoreCouponRuleObjs));
    }

    @Override
    public GoblinStoreMgtCouponInfoVo couponInfo(String storeId, String storeCouponId) {
        GoblinStoreMgtCouponInfoVo mgtCouponInfoVo = GoblinStoreMgtCouponInfoVo.getNew();
        GoblinStoreCouponVo couponVo = goblinRedisUtils.getStoreCouponVo(storeCouponId);
        if (null != couponVo && couponVo.getStoreId().equals(storeId)) {
            mgtCouponInfoVo.setCouponVo(couponVo);
            if ("1".equals(couponVo.getUseScope())) {
                List<String> spuIds = goblinRedisUtils.getStoreCouponSpuIds(storeCouponId);
                if (!CollectionUtils.isEmpty(spuIds)) {
                    List<GoblinSelfGoodsCategoryVo> selfGoodsCategoryVos = goblinStoreMgtExtraService.listCategoryVo();

                    ArrayList<GoblinStoreMgtCouponSpuListVo> couponSpuListVos = ObjectUtil.getGoblinStoreMgtCouponSpuListVoArrayList();
                    spuIds.forEach(spuId -> {
                        GoblinGoodsInfoVo goodsInfoVo = goblinRedisUtils.getGoodsInfoVo(spuId);
                        if (null != goodsInfoVo && goodsInfoVo.getDelFlg().equals("0")) {
                            String cateFid = goodsInfoVo.getCateFid(), cateSid = goodsInfoVo.getCateSid(), cateTid = goodsInfoVo.getCateTid();
                            List<GoblinSelfGoodsCategoryVo> categoryVoList = selfGoodsCategoryVos.stream()
                                    .filter(cr -> Arrays.asList(cateFid, cateSid, cateTid).contains(cr.getCateId())).collect(Collectors.toList());
                            categoryVoList.forEach(cr -> {
                                if (cr.getCateId().equals(cateFid)) goodsInfoVo.setCateFid(cr.getName());
                                if (cr.getCateId().equals(cateSid)) goodsInfoVo.setCateSid(cr.getName());
                                if (cr.getCateId().equals(cateTid)) goodsInfoVo.setCateTid(cr.getName());
                            });

                            couponSpuListVos.add(GoblinStoreMgtCouponSpuListVo.getNew().copy(goodsInfoVo));
                        }
                    });
                    mgtCouponInfoVo.setSpuVoList(couponSpuListVos);
                }
            }
        }
        return mgtCouponInfoVo;
    }

    @Override
    public boolean couponEdit(String uid, GoblinStoreCouponBasicVo storeCouponBasicVo) {
        String storeCouponId = storeCouponBasicVo.getStoreCouponId();
        LocalDateTime now = LocalDateTime.now();

        boolean activityStopFlg = storeCouponBasicVo.getEndTime().isBefore(now);

        storeCouponBasicVo.setState(activityStopFlg ? "2" : storeCouponBasicVo.getState());
        storeCouponBasicVo.setUpdatedAt(now);
        storeCouponBasicVo.setUpdatedBy(uid);

        if (goblinMongoUtils.updateMgtStoreCouponBasicVo(storeCouponBasicVo)) {
            if (activityStopFlg) {// 活动已结束，处理店铺活动数据
                GoblinStoreMarketDto storeMarketDto = GoblinStoreMarketDto.getNew();
                storeMarketDto.setId(storeCouponId);
                storeMarketDto.setType(1);

                goblinRedisUtils.delStoreMarketDto(storeCouponBasicVo.getStoreId(), storeMarketDto);
            }

            goblinRedisUtils.del(GoblinRedisConst.STORE_COUPON.concat(storeCouponId));// 删除REDIS缓存
            goblinRedisUtils.del(GoblinRedisConst.STORE_COUPON_RULE.concat(storeCouponId));// 删除REDIS缓存

            LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
            toMqSqls.add(SqlMapping.get("goblin_store_coupon.update"));
            LinkedList<Object[]> updateStoreCouponObjs = CollectionUtil.linkedListObjectArr();
            updateStoreCouponObjs.add(new Object[]{
                    storeCouponBasicVo.getTitle(), storeCouponBasicVo.getLabel(), storeCouponBasicVo.getNotice(),
                    storeCouponBasicVo.getType(), storeCouponBasicVo.getTriggers(), storeCouponBasicVo.getValFace(),
                    storeCouponBasicVo.getDiscount(), storeCouponBasicVo.getValOver(), storeCouponBasicVo.getValMinus(),
                    storeCouponBasicVo.getDeduction(), storeCouponBasicVo.getReceiveLimit(), storeCouponBasicVo.getReceiveCurb(),
                    storeCouponBasicVo.getUseScope(), storeCouponBasicVo.getStartTime(), storeCouponBasicVo.getEndTime(),
                    storeCouponBasicVo.getUpdatedBy(), storeCouponBasicVo.getUpdatedAt(), storeCouponId
            });
            toMqSqls.add(SqlMapping.get("goblin_store_coupon_rule.update_del"));
            LinkedList<Object[]> delStoreCouponRuleObjs = CollectionUtil.linkedListObjectArr();
            delStoreCouponRuleObjs.add(new Object[]{uid, now, storeCouponId});
            toMqSqls.add(SqlMapping.get("goblin_store_coupon_rule.insert"));
            LinkedList<Object[]> initStoreCouponRuleObjs = CollectionUtil.linkedListObjectArr();
            if ("1".equals(storeCouponBasicVo.getUseScope()) && !CollectionUtils.isEmpty(storeCouponBasicVo.getSpuIdList())) {// 部分商品
                storeCouponBasicVo.getSpuIdList().forEach(spuId -> initStoreCouponRuleObjs.add(new Object[]{storeCouponId, spuId, uid, now}));
            }

            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_COUPON.getKey(),
                    SqlMapping.gets(toMqSqls, updateStoreCouponObjs, delStoreCouponRuleObjs, initStoreCouponRuleObjs));
            return true;
        }
        return false;
    }

    @Override
    public boolean couponEditStock(GoblinStoreCouponVo storeCouponVo, String uid, int operStock) {
        String storeCouponId = storeCouponVo.getStoreCouponId();
        Integer stock = storeCouponVo.getStock();
        int surplusStock, operStockVal = Math.abs(operStock);
        if (operStock < 0) {
            surplusStock = goblinRedisUtils.decrStoreCouponStock(storeCouponId, operStockVal);
            if (surplusStock < 0) {
                goblinRedisUtils.incrStoreCouponStock(storeCouponId, operStock);
                log.warn("商铺活动:优惠券库存编辑:[storeCouponId={},operStock={},surplusStock={},uid={}]", storeCouponId, operStock, surplusStock, uid);
                return false;
            }
            stock -= operStockVal;
        } else {
            surplusStock = goblinRedisUtils.incrStoreCouponStock(storeCouponId, operStockVal);
            stock += operStockVal;
        }
        log.info("商铺活动:优惠券库存编辑:[storeCouponId={},operStock={},surplusStock={},uid={}]", storeCouponId, operStock, surplusStock, uid);
        LocalDateTime now = LocalDateTime.now();

        goblinMongoUtils.updateMgtStoreCouponStock(storeCouponId, stock, uid, now);
        if (stock.equals(0)) {
            goblinRedisUtils.delStoreCouponStock(storeCouponId);
        }
        goblinRedisUtils.del(GoblinRedisConst.STORE_COUPON.concat(storeCouponId));// 删除REDIS缓存

        queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_COUPON.getKey(),
                SqlMapping.get("goblin_store_coupon.update_stock", stock, uid, now, storeCouponId));
        return true;
    }
}
