
package com.tmerclub.cloud.seckill.service.impl;

import com.tmerclub.cloud.api.order.feign.OrderFeignClient;
import com.tmerclub.cloud.api.product.dto.StockBillLogItemDTO;
import com.tmerclub.cloud.api.product.feign.SpuFeignClient;
import com.tmerclub.cloud.common.cache.bo.MultiDecrementBO;
import com.tmerclub.cloud.common.cache.enums.MultiStockEnum;
import com.tmerclub.cloud.common.cache.manager.MultiRedisStockManager;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.order.bo.RedisSeckillOrderBO;
import com.tmerclub.cloud.common.order.vo.OrderActivityVO;
import com.tmerclub.cloud.common.order.vo.ShopCartItemVO;
import com.tmerclub.cloud.common.order.vo.ShopCartOrderMergerVO;
import com.tmerclub.cloud.common.order.vo.ShopCartOrderVO;
import com.tmerclub.cloud.common.product.vo.GiveawaySpuVO;
import com.tmerclub.cloud.common.product.vo.GiveawayVO;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.rocketmq.config.RocketMqConstant;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.seckill.service.SeckillOrderService;
import com.tmerclub.cloud.seckill.vo.SeckillVO;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 秒杀订单
 *
 * @author lhd
 * @date 2021-03-30 14:59:28
 */
@Service
public class SeckillOrderServiceImpl implements SeckillOrderService {
    private static final Logger logger = LoggerFactory.getLogger(SeckillOrderServiceImpl.class);

    @Autowired
    private MultiRedisStockManager multiRedisStockManager;
    @Autowired
    private RocketMQTemplate seckillOrderCreateTemplate;
    @Autowired
    private RocketMQTemplate outStockLogTemplate;
    @DubboReference
    private SpuFeignClient spuFeignClient;
    @DubboReference
    private OrderFeignClient orderFeignClient;

    @Override
    public boolean checkOrderSpuNum(SeckillVO seckill, Long userId, Integer count) {
        // 判断之前秒杀的商品有没有超过限制，-1表示商品不限制秒杀数量
        if (!Objects.equals(seckill.getMaxNum(), -1)) {
            // 获取当前库存所使用的redis
            Integer buyNum = multiRedisStockManager.getSeckillProdBuyCount(new MultiDecrementBO(MultiStockEnum.SECKILL,
                    userId,null,seckill.getSeckillId(), null,null,null,null, null));
            // 本次秒杀商品限购
            return seckill.getMaxNum() >= count + buyNum;
        }
        return true;
    }

    /**
     * 锁定库存
     * 纯redis的操作，不需要事务
     * 具体步骤：
     * 1.发送库存解锁mq
     * 2.扣除库存，同时添加锁定记录
     * 4.添加出库记录
     * 正确流程，发送mq（秒杀库存解锁+订单超时+出库记录）后，扣除扣除库存及在redis生成订单
     * 存在问题及处理方案（解锁库存时处理）：
     * 1.发送mq（删除出库记录+订单超时+出库记录）任意失败 - 无需处理
     * 2.发送mq（删除出库记录+订单超时+出库记录）后，扣除库存及在redis生成订单失败 - 删除出库记录
     * 3.发送mq（删除出库记录+订单超时+出库记录）后，成功扣除扣除库存及在redis生成订单 - 不用处理
     *
     */
    @Override
    public void submit(ShopCartOrderMergerVO mergerOrder, Long seckillId, Integer maxNum) {
        ShopCartOrderVO shopCartOrderVO = mergerOrder.getShopCartOrders().get(0);
        ShopCartItemVO shopCartItemVO = shopCartOrderVO.getShopCartItemDiscounts().get(0).getShopCartItems().get(0);

        List<StockBillLogItemDTO> stockBillLogItems = getStockBillLogItems(shopCartOrderVO, shopCartItemVO);

        RedisSeckillOrderBO redisSeckillOrderBO = new RedisSeckillOrderBO();
        redisSeckillOrderBO.setUserId(mergerOrder.getUserId());
        redisSeckillOrderBO.setOrderId(shopCartOrderVO.getOrderId());
        // 2.发送延时消息，订单超时mq
        SendStatus sendOrderStatus = seckillOrderCreateTemplate.syncSend(RocketMqConstant.SECKILL_ORDER_TIME_OUT_CANCEL_TOPIC, new GenericMessage<>(redisSeckillOrderBO), RocketMqConstant.TIMEOUT, RocketMqConstant.CANCEL_ORDER_DELAY_LEVEL).getSendStatus();
        if (!Objects.equals(sendOrderStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，发的出去无所谓
            throw new LuckException(ResponseEnum.EXCEPTION);
        }

        // 3.添加出库记录，为了防止出库明细添加失败，导致订单创建，但是如果在用户提交订单时失败了会产生脏数据，所以先发送订单出库记录，在执行订单创建
        // 生成出库明细
        SendStatus sendStockStatus = outStockLogTemplate.syncSend(RocketMqConstant.OUT_STOCK_LOG_TOPIC, new GenericMessage<>(stockBillLogItems)).getSendStatus();
        if (!Objects.equals(sendStockStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，发的出去无所谓
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        logger.info("tryLockStock stockBillLogItems:{}", stockBillLogItems);

        // 4.更新秒杀sku库存，在redis中创建订单
        MultiDecrementBO multiDecrementBO = new MultiDecrementBO(MultiStockEnum.SECKILL, mergerOrder.getUserId(), shopCartOrderVO.getOrderId(), seckillId, shopCartItemVO.getSkuId(), shopCartItemVO.getCount(), maxNum, Json.toJsonString(mergerOrder), null);
        multiDecrementBO.setStockPointId(shopCartItemVO.getStockPointId());
        multiRedisStockManager.decrStocks(multiDecrementBO);
//        // dev 4.更新秒杀sku库存，在redis中创建订单
//        multiRedisStockManager.decrStocks(new MultiDecrementBO(MultiStockEnum.SECKILL, mergerOrder.getUserId(), shopCartOrderVO.getOrderId(), seckillId, shopCartItemVO.getSkuId(), shopCartItemVO.getCount(), maxNum, Json.toJsonString(mergerOrder)));

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccessOrderByOrderId(Long orderId) {
        ServerResponseEntity<OrderActivityVO> activityOrderPayInfo = orderFeignClient.getActivityOrderInfo(orderId);
        if (!activityOrderPayInfo.isSuccess()) {
            throw new LuckException(activityOrderPayInfo.getMsg());
        }
        OrderActivityVO order = activityOrderPayInfo.getData();
        if (Objects.isNull(order)) {
            return;
        }
        if (Objects.equals(order.getIsPayed(), 0)) {
            return;
        }
        // 订单支付成功后，删除redis中的订单和订单锁定信息
        multiRedisStockManager.handlePaySuccessSecKillStock(order.getCount(),orderId,order.getUserId());
//        // 秒杀订单支付成功后，增加销量
//        spuFeignClient.updateSaleNumBySeckillOrder(order.getSpuId(), order.getCount());
    }

    private static List<StockBillLogItemDTO> getStockBillLogItems(ShopCartOrderVO shopCartOrderVO, ShopCartItemVO shopCartItemVO) {
        Map<Long, StockBillLogItemDTO> stockBillLogItemMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        StockBillLogItemDTO stockBillLogItem = stockBillLogItemMap.get(shopCartItemVO.getSkuId());
        if (Objects.isNull(stockBillLogItem)) {
            stockBillLogItem = new StockBillLogItemDTO(
                    shopCartItemVO.getSpuId(),
                    shopCartItemVO.getSkuId(),
                    shopCartItemVO.getShopId(),
                    shopCartOrderVO.getOrderId(),
                    shopCartItemVO.getSpuName(),
                    shopCartItemVO.getSkuName(),
                    0,
                    shopCartItemVO.getSkuPriceFee(),
                    shopCartItemVO.getPartyCode(),
                    shopCartItemVO.getImgUrl()
            );
            stockBillLogItemMap.put(stockBillLogItem.getSkuId(), stockBillLogItem);
        }
        stockBillLogItem.setStockCount(stockBillLogItem.getStockCount() + shopCartItemVO.getCount());

        // 如果是供应商商品且发货方式为供应商发货，则将spuId和skuId替换成供应商商品的
        // 只替换id，商品名称图片等信息则保留商家的商品信息，库存记录是商家的库存记录
        if (!Objects.equals(shopCartItemVO.getSupplierId(), 0L) && Objects.equals(shopCartItemVO.getSupplierDeliveryType(), 1)) {
            stockBillLogItem.setSpuId(shopCartItemVO.getSupplierSpuId());
            stockBillLogItem.setSkuId(shopCartItemVO.getSupplierSkuId());
        }

        //处理赠品订单项
        GiveawayVO giveaway = shopCartItemVO.getGiveaway();
        if(Objects.isNull(giveaway)){
            return new ArrayList<>(stockBillLogItemMap.values());
        }
        // 赠品库存计算
        for (GiveawaySpuVO giveawaySpuVO : giveaway.getGiveawaySpus()) {
            StockBillLogItemDTO giveawayStockBillLogItem = stockBillLogItemMap.get(giveawaySpuVO.getSkuId());
            if (Objects.isNull(giveawayStockBillLogItem)) {
                giveawayStockBillLogItem = new StockBillLogItemDTO(
                        giveawaySpuVO.getSpuId(),
                        giveawaySpuVO.getSkuId(),
                        giveawaySpuVO.getShopId(),
                        shopCartOrderVO.getOrderId(),
                        giveawaySpuVO.getSpuName(),
                        giveawaySpuVO.getSkuName(),
                        0,
                        giveawaySpuVO.getPriceFee(),
                        giveawaySpuVO.getPartyCode(),
                        giveawaySpuVO.getMainImgUrl()
                );
                stockBillLogItemMap.put(giveawaySpuVO.getSkuId(), giveawayStockBillLogItem);
            }
            giveawayStockBillLogItem.setStockCount(giveawayStockBillLogItem.getStockCount() + giveawaySpuVO.getGiveawayNum());
        }
        // 库存日志列表
        return new ArrayList<>(stockBillLogItemMap.values());
    }
}
