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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinGoodsSkuInfoVo;
import com.liquidnet.service.goblin.dto.vo.GoblinNftExCodeVo;
import com.liquidnet.service.goblin.dto.vo.GoblinNftExSkuVo;
import com.liquidnet.service.goblin.entity.*;
import com.liquidnet.service.goblin.mapper.*;
import com.liquidnet.service.goblin.param.GoblinNftExSkuParam;
import com.liquidnet.service.goblin.service.IGoblinNftExSkuService;
import com.liquidnet.service.platform.utils.GoblinRedisUtils;
import com.liquidnet.service.platform.utils.ObjectUtil;
import com.microsoft.schemas.office.visio.x2012.main.ShapeSheetType;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @Author: wll
 * @Description: nft兑换活动和和sku的关联
 * @Date:Create：in 2022/4/19 5:14 下午
 */

@Service
@Slf4j
public class GoblinNftExSkuServiceImpl implements IGoblinNftExSkuService {

    @Autowired
    private GoblinRedisUtils goblinRedisUtils;
    @Autowired
    private GoblinGoodsSkuMapper goblinGoodsSkuMapper;
    @Autowired
    GoblinNftExCodeTaskMapper goblinNftExCodeTaskMapper;
    @Autowired
    private GoblinNftExCodeTaskRelationMapper goblinNftExCodeTaskRelationMapper;


    @Transactional
    @Override
    public ResponseDto<Object> add(List<GoblinNftExSkuParam> goblinNftExSkuParams) {

        LocalDateTime now = LocalDateTime.now();

        //ArrayList<GoblinNftExSkuVo> goblinNftExSkuArrayList = ObjectUtil.getGoblinNftExSkuArrayList();
        //FArrayList<GoblinNftExCodeVo> goblinNftExCodeArrayList = ObjectUtil.getGoblinNftExCodeArrayList();

        // 是否存在盲盒
        Boolean bol = false;

        // 配置库存
        Integer stockNumber = 0;
        StringBuffer spuIds = new StringBuffer();
        StringBuffer skuIds = new StringBuffer();

        for (GoblinNftExSkuParam goblinNftExSkuParam : goblinNftExSkuParams) {
            if (goblinNftExSkuParam.getUnbox().equals("1")) {
                bol = true;
                spuIds.append(goblinNftExSkuParam.getSpuId()).append(",");
            }
            stockNumber += goblinNftExSkuParam.getExStock();
            skuIds.append(goblinNftExSkuParam.getSkuId()).append(",");
        }

        if (stockNumber <= 0) {
            return ResponseDto.failure("配置库存为0，无法生成!");
        }

        if (StringUtil.isBlank(skuIds)) {
            return ResponseDto.failure("sku数据不可传空!");
        }


        // 获取sku数据校验是否正确
        List<GoblinGoodsSku> goblinGoodsSkuListCheck = goblinGoodsSkuMapper.selectBySkuIds(skuIds.toString());


        ArrayList<String> skuIdList = CollectionUtil.arrayListString();
        for (GoblinGoodsSku goblinGoodsSku : goblinGoodsSkuListCheck) {
            // 不能购买的过滤
            if (getSkuAllStatusShow(goblinGoodsSku)) {
                skuIdList.add(goblinGoodsSku.getSkuId());
            }
        }

        if (skuIdList.size() <= 0) {
            return ResponseDto.failure("当前选中的sku不处于可售卖状态!");
        }


        // sku --> 概率
        HashMap<String, BigDecimal> map = CollectionUtil.mapStringBigDecimal();

        if (bol) {
            // 获取spu下所有sku
            List<GoblinGoodsSku> goblinGoodsSkus = goblinGoodsSkuMapper.selectBySpuIds(spuIds.toString());
            int countStockNumber = 0;
            int checkNumberSku = 0;
            for (GoblinGoodsSku goblinGoodsSku : goblinGoodsSkus) {
                if (goblinGoodsSku.getUnbox().equals("1")) {
                    continue;
                }
                // 判断开售、停售时间
                LocalDateTime saleStartTime = goblinGoodsSku.getSaleStartTime();
                LocalDateTime saleStopTime = goblinGoodsSku.getSaleStopTime();
                LocalDateTime nowTime = LocalDateTime.now();
                if (nowTime.isBefore(saleStartTime)) {
                    continue;
                } else if (null != saleStopTime && nowTime.isAfter(saleStopTime)) {
                    continue;
                }

                //是否购买
                if (!goblinGoodsSku.getSkuCanbuy().equals("1")){
                    continue;
                }
                // 不能购买的 没库存的 概率是0的 过滤
                if (getSkuAllStatusShow(goblinGoodsSku) && goblinRedisUtils.getSkuAllStatusStock(goblinGoodsSku) > 0 && goblinGoodsSku.getHitRatio() != null) {
                    countStockNumber += goblinRedisUtils.getSkuStock(goblinGoodsSku.getSkuId());
                    map.put(goblinGoodsSku.getSkuId(), goblinGoodsSku.getHitRatio());
                    checkNumberSku++;
                }
            }

            if (checkNumberSku <= 0){
                return ResponseDto.failure("盲盒下没有可生成兑换码的sku!");
            }

            if (countStockNumber < stockNumber) {
                ResponseDto.failure("配置库存大于sku总库存!");
            }

        }


        List<GoblinNftExCodeTask> goblinNftExCodeTasks = ObjectUtil.getGoblinNftExCodeTaskArrayList();



        // 构建对象
        for (GoblinNftExSkuParam goblinNftExSkuParam : goblinNftExSkuParams) {

            if (!goblinNftExSkuParam.getUnbox().equals("1") && !skuIdList.contains(goblinNftExSkuParam.getSkuId())) {
                continue;
            }

            // 定时任务对象构建
            GoblinNftExCodeTask goblinNftExCodeTask = GoblinNftExCodeTask.getNew();
            goblinNftExCodeTask.setTaskId(IDGenerator.nextSnowId());
            goblinNftExCodeTask.setActivityId(goblinNftExSkuParam.getActivityId());
            goblinNftExCodeTask.setStoreId(goblinNftExSkuParam.getStoreId());
            goblinNftExCodeTask.setSpuId(goblinNftExSkuParam.getSpuId());
            goblinNftExCodeTask.setSkuId(goblinNftExSkuParam.getSkuId());
            goblinNftExCodeTask.setUnbox(goblinNftExSkuParam.getUnbox());
            goblinNftExCodeTask.setExStock(goblinNftExSkuParam.getExStock());
            goblinNftExCodeTask.setExLimit(goblinNftExSkuParam.getExLimit());
            goblinNftExCodeTask.setExStartTime(goblinNftExSkuParam.getExStartTime());
            goblinNftExCodeTask.setExStopTime(goblinNftExSkuParam.getExStopTime());
            goblinNftExCodeTask.setTyp(1);
            goblinNftExCodeTask.setCreatedAt(now);
            if (goblinNftExSkuParam.getUnbox().equals("1")) {

                ArrayList<GoblinNftExCodeTaskRelation> goblinNftExCodeTaskRelations = ObjectUtil.getGoblinNftExCodeTaskRelationArrayList();

                // sku ---> 库存
                Map<String, Integer> skuMap = getSkuHitRatio(goblinNftExSkuParam.getExStock(), map);

                if (skuMap.size() > 0) {
                    Integer sNumber = 0;
                    for (String key : skuMap.keySet()) {
                        Integer stockNum = skuMap.get(key);
                        // 根据sku减库存
                        int stock = goblinRedisUtils.decrSkuStock(null, key, stockNum);
                        if (stock < 0) {
                            goblinRedisUtils.incrSkuStock(null, key, stockNum);
                        } else {
                            GoblinNftExCodeTaskRelation goblinNftExCodeTaskRelation = GoblinNftExCodeTaskRelation.getNew();
                            goblinNftExCodeTaskRelation.setTaskId(goblinNftExCodeTask.getTaskId());
                            goblinNftExCodeTaskRelation.setSkuId(key);
                            goblinNftExCodeTaskRelation.setStock(stockNum);
                            goblinNftExCodeTaskRelation.setCreatedAt(now);
                            goblinNftExCodeTaskRelations.add(goblinNftExCodeTaskRelation);
                        }
                        sNumber += stockNum;
                    }
                    goblinNftExCodeTaskRelationMapper.inserts(goblinNftExCodeTaskRelations);
                    goblinNftExCodeTask.setExStock(sNumber);
                }

            } else {
                int stock = goblinRedisUtils.decrSkuStock(null, goblinNftExSkuParam.getSkuId(), goblinNftExSkuParam.getExStock());
                if (stock < 0) {
                    goblinRedisUtils.incrSkuStock(null, goblinNftExSkuParam.getSkuId(), goblinNftExSkuParam.getExStock());
                }
            }

            goblinNftExCodeTasks.add(goblinNftExCodeTask);
        }
        // 数据库操作
        long startm = System.currentTimeMillis();
        if (goblinNftExCodeTasks.size() > 0) {
            goblinNftExCodeTaskMapper.addGoblinNftExCodeTasks(goblinNftExCodeTasks);
        }
        // goblinNftExCodeMapper.addGoblinNftExCodes(goblinNftExCodes);
        log.debug("MSQ耗时：ms", System.currentTimeMillis() - startm);

        return ResponseDto.success(true);
    }

    /**
     * 获取每个sku的兑换码生成数量
     * <p>
     * sku兑换码数 = 生码总数 * sku概率/总概率
     * 若sku兑换码数 > sku剩余库存
     * 用 sku兑换码数 - sku剩余库存 * (其他sku概率/其他总概率)
     * <p>
     * 例：生成兑换码 120 有sku1,sku2,sku3
     * sku1 库存5 概率 10%
     * sku2 库存100 概率 20%
     * sku3 库存100 概率 30%
     * <p>
     * sku1兑换码数=120 * (10/60) = 20   >5
     * sku2兑换码数=120 * (20/60) = 40 <100
     * sku3兑换码数=120 * (30/60) = 60 <100
     * <p>
     * sku2第二轮兑换码数 = (20-5) * (20/50) = 6 < 60
     * sku3第二轮兑换码数 = (20-5) * (30/50) = 9 < 40
     * <p>
     * 总兑换码 = sku1兑换码数+sku2兑换码数+sku3兑换码数+sku2第二轮兑换码数+sku3第二轮兑换码数
     *
     * @return
     */
    private Map<String, Integer> getSkuHitRatio(Integer exStock, Map<String, BigDecimal> map) {

        // 获取sku总概率
        BigDecimal hitRatioCount = new BigDecimal(0);


        // Map<String, Map<String, Object>> mapMap = new HashMap<>();

        // sku --> 概率/库存
        Integer countStockNumber = 0;
        HashMap<String, Map<String, Object>> mapMap = CollectionUtil.mapHashMap();
        for (String key : map.keySet()) {
            BigDecimal skuHitRatio = BigDecimal.valueOf(Double.valueOf(map.get(key).toString()));
            HashMap<String, Object> objectMap = CollectionUtil.mapStringObject();
            if (skuHitRatio == null) {
                continue;
            }
            hitRatioCount = hitRatioCount.add(skuHitRatio);
            objectMap.put("hitRatio", skuHitRatio);
            objectMap.put("stock", goblinRedisUtils.getSkuStock(key));
            countStockNumber += goblinRedisUtils.getSkuStock(key);
            mapMap.put(key, objectMap);

        }


        // sku ---> 库存
        HashMap<String, Integer> stockMap = CollectionUtil.mapStringInteger();
        ArrayList<String> eliminateSkuIdList = CollectionUtil.arrayListString();
        getStock(exStock, hitRatioCount, mapMap, stockMap, countStockNumber, eliminateSkuIdList);
        return stockMap;
    }

    public static void main(String[] args) {

        Map<String, Object> map = new HashMap<>();
        map.put("1", new BigDecimal("0.11"));
        System.out.println(BigDecimal.valueOf(Double.valueOf(map.get("1").toString())));

        Map<String, Map<String, Object>> mapMap = new HashMap<>();
        Map<String, Object> objectMap = new HashMap<>();
        objectMap.put("hitRatio", 20);
        objectMap.put("stock", 1000);
        mapMap.put("1", objectMap);

        Map<String, Object> objectMap1 = new HashMap<>();
        objectMap1.put("hitRatio", 30);
        objectMap1.put("stock", 4);
        // mapMap.put("2", objectMap1);

        Map<String, Object> objectMap2 = new HashMap<>();
        objectMap2.put("hitRatio", 30);
        objectMap2.put("stock", 1000);
        // mapMap.put("3", objectMap2);

        Map<String, Object> objectMap3 = new HashMap<>();
        objectMap3.put("hitRatio", 10);
        objectMap3.put("stock", 1000);
        // mapMap.put("4", objectMap3);

        Map<String, Integer> stockMap = new HashMap<>();
        ArrayList<String> eliminateSkuIdList = CollectionUtil.arrayListString();
        getStock(7, new BigDecimal(20), mapMap, stockMap, 1032, eliminateSkuIdList);
        for (String key : stockMap.keySet()) {
            System.out.println("skuId" + key + "***********数量" + stockMap.get(key));
        }
        System.out.println("kkk");
    }

    /**
     * 获取单个sku库存
     *
     * @param exStock
     * @param hitRatioCount
     * @param mapMap
     * @return
     */
    private static void getStock(Integer exStock, BigDecimal hitRatioCount, Map<String, Map<String, Object>> mapMap, Map<String, Integer> stockMap, Integer countStockNumber, ArrayList<String> eliminateSkuIdList) {
        if (exStock <= 0 || countStockNumber == 0) {
            return;
        }

        // 单个sku ---> 当前循环权重
        HashMap<String, BigDecimal> bigDecimalHashMap = CollectionUtil.mapStringBigDecimal();
        int subNumber = 0;
        for (String key : mapMap.keySet()) {
            Object stock = mapMap.get(key).get("stock");
            Object hitRatio = mapMap.get(key).get("hitRatio");
            if (Integer.valueOf(stock.toString()) <= 0) {
                continue;
            }

            BigDecimal bigDecimal = BigDecimal.valueOf(exStock).multiply((new BigDecimal(hitRatio.toString()).divide(hitRatioCount, 2, BigDecimal.ROUND_HALF_UP)));
            bigDecimalHashMap.put(key, bigDecimal);

            // 要减去的库存
            int subStock = BigDecimal.valueOf(exStock).multiply((new BigDecimal(hitRatio.toString()).divide(hitRatioCount, 2, BigDecimal.ROUND_HALF_UP))).setScale(0, BigDecimal.ROUND_UP).intValue();

            if (subStock > Integer.valueOf(stock.toString())) {
                subNumber += Integer.valueOf(stock.toString());
                countStockNumber -= Integer.valueOf(stock.toString());
                mapMap.get(key).put("stock", 0);
                if (stockMap.get(key) == null) {
                    stockMap.put(key, Integer.valueOf(stock.toString()));
                } else {
                    stockMap.put(key, stockMap.get(key) + Integer.valueOf(stock.toString()));
                }

            } else {
                countStockNumber -= subStock;
                mapMap.get(key).put("stock", Integer.valueOf(stock.toString()) - subStock);
                subNumber += subStock;
                if (stockMap.get(key) == null) {
                    stockMap.put(key, subStock);
                } else {
                    stockMap.put(key, stockMap.get(key) + subStock);
                }
            }
        }

        if (subNumber > exStock) {
            int usNumber = subNumber - exStock;
            ArrayList<String> skuIdList = CollectionUtil.arrayListString();
            for (int k = 0; k < usNumber; k++) {
                String subKey = "";
                double maxValue = Double.MAX_VALUE;
                for (String key : bigDecimalHashMap.keySet()) {
                    if (skuIdList.contains(key)) {
                        continue;
                    }
                    Double compare = Double.valueOf(bigDecimalHashMap.get(key).toString());
                    if (maxValue > compare) {
                        maxValue = compare;
                        subKey = key;
                    }
                }
                Integer stock = stockMap.get(subKey);
                stockMap.put(subKey, stock - 1);
                skuIdList.add(subKey);
            }

            return;
        }

        exStock -= subNumber;
        if (exStock > 0) {
            for (String key : mapMap.keySet()) {
                Object hitRatio = mapMap.get(key).get("hitRatio");
                Object stock = mapMap.get(key).get("stock");
                if (Integer.valueOf(stock.toString()) <= 0 && !eliminateSkuIdList.contains(key)) {
                    eliminateSkuIdList.add(key);
                    hitRatioCount = hitRatioCount.subtract(new BigDecimal(hitRatio.toString()));
                }
            }
            getStock(exStock, hitRatioCount, mapMap, stockMap, countStockNumber, eliminateSkuIdList);
        }
    }


    // 各种状态下判断藏品是否可以展示
    public boolean getSkuAllStatusShow(GoblinGoodsSku info) {
        if (
                info != null
                        && info.getSkuType() == 1
                        && info.getStatus().equals("3")
                        && info.getShelvesStatus().equals("3")
                        // && (info.getSkuAppear() == null || info.getSkuAppear().equals("0"))
                        && info.getDelFlg().equals("0")
                        ) {
            return true;
        } else {
            return false;
        }
    }


}
