package com.yami.shop.seckill.api.service.impl;

import cn.hutool.core.lang.Snowflake;
import com.yami.shop.common.exception.GlobalException;
import com.yami.shop.common.util.IdUtil;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.seckill.api.service.SeckillCacheManager;
import com.yami.shop.seckill.comment.service.SeckillSkuService;
import lombok.AllArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * @author LGH
 */
@Service
@AllArgsConstructor
public class SeckillCacheManagerImpl implements SeckillCacheManager {

    private final RedissonClient redissonClient;

    private static final String REDISSON_LOCK_PREFIX = "redisson_lock:";
    /**
     * 秒杀限流前缀
     */
    private final static String SECKILL_LIMIT_PREFIX = "SECKILL_LIMIT_";

    /**
     * 秒杀路径前缀
     */
    private final static String SECKILL_PATH_PREFIX = "SECKILL_PATH_";

    private final static String SECKILL_SKU_STOCKS_PREFIX = "SECKILL_SKU_STOCKS_";

    private final Snowflake snowflake;

    private final SeckillSkuService seckillSkuService;


    @Override
    public String createOrderPath(String userId) {

        String limitKey = SECKILL_LIMIT_PREFIX + userId;
        int maxCount = 5;

        // 秒杀次数+1
        long seckillNum = RedisUtil.incr(limitKey, 1);

        // 5秒只能提交5次请求
        if (seckillNum == 1) {
            // 5秒后失效
            RedisUtil.expire(limitKey, 5);
        }
        if (seckillNum >= maxCount) {
            throw new GlobalException("请求过频繁，请稍后再试");
        }
        String orderPath = IdUtil.encode(snowflake.nextId());

        // 保存秒杀路径，5分钟这个路径就失效
        RedisUtil.set(getPathKey(userId), orderPath, 300);

        return orderPath;
    }

    @Override
    public void checkOrderPath(String userId, String orderPath) {
        String cacheOrderPath = RedisUtil.get(getPathKey(userId));

        if (!Objects.equals(cacheOrderPath,orderPath)) {
            throw new GlobalException("订单已过期，请重新选择商品进行秒杀");
        }
    }

    private String getPathKey(String userId) {
        return SECKILL_PATH_PREFIX + userId;
    }


    @Override
    public void decrSeckillSkuStocks(Long seckillSkuId,Long seckillId, Integer prodCount) {
        String key = SECKILL_SKU_STOCKS_PREFIX + seckillSkuId;

        Long cacheStocks = RedisUtil.getLongValue(key);

        // 如果没有库存就缓存一个库存
        if (cacheStocks == null || RedisUtil.getExpire(key) < 1) {

            // 加锁，防止缓存击穿
            RLock rLock = redissonClient.getLock(REDISSON_LOCK_PREFIX + ":getSeckillSkuStocks");
            try {
                if (rLock.tryLock(10,10, TimeUnit.SECONDS)){
                    // 再获取一遍缓存
                    cacheStocks = RedisUtil.getLongValue(key);
                    if (cacheStocks == null) {
                        seckillSkuService.removeSeckillCacheSkuById(seckillSkuId,seckillId);
                        Integer seckillStocks = seckillSkuService.getSeckillSkuById(seckillSkuId).getSeckillStocks();
                        // 秒杀实时性要求比较高，但是不能缓存时间太短呀，不然锁竞争很激烈的呀，缓存120秒
                        RedisUtil.setLongValue(key, Long.valueOf(seckillStocks),120);
                        cacheStocks = Long.valueOf(seckillStocks);
                    }
                } else {
                    throw new GlobalException("网络异常");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (rLock.isLocked()) {
                        rLock.unlock();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

        if (cacheStocks == null || cacheStocks < prodCount || RedisUtil.decr(key, prodCount) < 0) {

            throw new GlobalException("本轮商品已被秒杀完毕，还有用户未支付，还有机会哟");
        }

    }


}
