package com.liquidnet.service.order.utils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.candy.vo.CandyUseResultVo;
import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;

@Component
@Slf4j
public class GoblinNftOrderUtils {

    @Value("${liquidnet.service.candy.url}")
    private String candyUrl;
    @Value("${liquidnet.service.order.url-pay.nftRefundNotify}")
    private String synUrl;
    @Value("${liquidnet.service.dragon.urls.refundApply}")
    private String refundApply;

    @Autowired
    private QueueUtils queueUtils;
    @Autowired
    private GoblinRedisUtils goblinRedisUtils;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private GoblinMongoUtils goblinMongoUtils;
    @Autowired
    private GoblinOrderUtils goblinOrderUtils;

    // 库存
    public int decrSkuStock(String skuId, Integer stock) {
        String redisKey = GoblinRedisConst.REAL_STOCK_SKU.concat(skuId);
        return (int) redisUtil.decr(redisKey, stock);
    }

    public int incrSkuStock(String skuId, Integer stock) {
        String redisKey = GoblinRedisConst.REAL_STOCK_SKU.concat(skuId);
        return (int) redisUtil.incr(redisKey, stock);
    }

    // 回滚用户sku购买个数和库存
    public void backSkuCountAndStock(String uid, String stockSkuId, String bySkuId, int number) {
        // 减少用户购买个数
        goblinRedisUtils.decrSkuCountByUid(uid, bySkuId, number);
        // 增加库存
        incrSkuStock(stockSkuId, number);
    }

    // 订单详情vo
    public void setNftOrder(GoblinNftOrderVo vo) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_INFO.concat(vo.getOrderId());
        redisUtil.set(redisKey, vo);
    }

    // 获取 订单详情vo
    public GoblinNftOrderVo getNftOrder(String orderId) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_INFO.concat(orderId);
        Object obj = redisUtil.get(redisKey);
        if (obj == null) {
            return null;
        } else {
            return (GoblinNftOrderVo) obj;
        }
    }

    // code换id
    public void setNftOrderIdOfCode(String orderCode, String orderId) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_ID_OF_CODE.concat(orderCode);
        redisUtil.set(redisKey, orderId);
    }
    // code换id
    public String getNftOrderIdOfCode(String orderCode) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_ID_OF_CODE.concat(orderCode);
        Object obj = redisUtil.get(redisKey);
        if (obj == null) {
            return "";
        } else {
            return (String) obj;
        }
    }

    // 添加 订单id列表
    public void addNftOrderList(String uid, String orderId) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_USER_ID_LIST.concat(uid);
        List<String> list = getNftOrderList(uid);
        if (list.size() >= 40) {
            list.remove(0);
            list.add(orderId);
        } else {
            list.add(orderId);
        }
        redisUtil.set(redisKey, list);
    }

    // 获取 订单id列表
    public List<String> getNftOrderList(String uid) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_USER_ID_LIST.concat(uid);
        Object obj = redisUtil.get(redisKey);
        if (obj == null) {
            return CollectionUtil.arrayListString();
        } else {
            return (List<String>) obj;
        }
    }

    // 退款订单vo
    public void setBackOrderVo(GoblinNftOrderRefundVo vo) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_REFUND_INFO.concat(vo.getOrderId());
        redisUtil.set(redisKey, vo);
    }

    public boolean setNftOrderBuyLock(String userId) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_BUG_LOCK
                .concat(userId);
        return redisUtil.lock(redisKey, 1, 60);
    }

    public boolean setNftOrderExLock(String code) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_EX_LOCK
                .concat(code);
        return redisUtil.lock(redisKey, 1, 60);
    }

    public void delNftOrderBuyLock(String userId) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_BUG_LOCK
                .concat(userId);
        redisUtil.uLock(redisKey);
    }

    public void delNftOrderExLock(String code) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_EX_LOCK
                .concat(code);
        redisUtil.uLock(redisKey);
    }

    /**
     * 兑换码
     */
    public GoblinNftExCodeVo getGoblinNftExCodeVo(String code) {
        String redisKey = GoblinRedisConst.ACTIVITY_SKU_CODE.concat(code);
        Object obj = redisUtil.get(redisKey);
        if (obj == null) {
            return null;
        } else {
            return (GoblinNftExCodeVo) obj;
        }
    }
    public void setGoblinNftExCodeVo(GoblinNftExCodeVo codeVo) {
        String redisKey = GoblinRedisConst.ACTIVITY_SKU_CODE.concat(codeVo.getCode());
        redisUtil.set(redisKey, codeVo);
    }

    public int incrExCountByUid(String uid, String skuId, String activityId, int number) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_EX_BUY_COUNT.concat(activityId+':').concat(skuId+':').concat(uid);
        return (int) redisUtil.incr(redisKey, number);
    }
    public int decrExCountByUid(String uid, String skuId, String activityId, int number) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_EX_BUY_COUNT.concat(activityId+':').concat(skuId+':').concat(uid);
        return (int) redisUtil.decr(redisKey, number);
    }

    // skuId 是否开启预约
    public boolean getValueBySkuId(String skuId) {
        String redisKey = GoblinRedisConst.ANTICIPATE_VALUE_SKUID.concat(skuId);
        Object obj = redisUtil.get(redisKey);
        if (obj == null) {
            return false;
        } else {
            return true;
        }
    }
    // 是否预约过 没开启true
    public boolean getUserAboutAut(String skuId, String uid) {
        boolean valueBySkuId = getValueBySkuId(skuId);
        if (valueBySkuId) {
            String redisKey = GoblinRedisConst.USER_ANTICIPATE_STATE.concat(skuId).concat(uid);
            Object obj = redisUtil.get(redisKey);
            if (obj == null) {
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }

    /**
     * 使用平台优惠券 只支持代金券
     *
     * @param uCouponId  券id
     * @param content    消费内容
     * @param totalPrice 订单总价
     * @param spuId      spuId
     * @param uid        uid
     * @return
     */
    public GoblinUseResultVo useCoupon(String uCouponId, String content, BigDecimal totalPrice, String spuId, String uid) {
        MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString();
        params.add("uCouponId", uCouponId);
        params.add("content", content);
        params.add("totalPrice", totalPrice.toString());
        params.add("goodId", spuId);
        params.add("performanceId", "null");
        params.add("timeId", "null");
        params.add("ticketId", "null");
        params.add("uid", uid);

        MultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString();
        header.add("Authorization", "Bearer " + CurrentUtil.getToken());
        header.add("Accept", "application/json;charset=UTF-8");

        String returnData = HttpUtil.post(candyUrl.concat("/candy-coupon/use"), params, header);
        ResponseDto<CandyUseResultVo> useResultVo = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<CandyUseResultVo>>() {
        });
        CandyUseResultVo candyUseResultVo = useResultVo.getData();
        Integer type = candyUseResultVo.getCouType();
        BigDecimal value = candyUseResultVo.getValue();
        BigDecimal voucher;
        switch (type) {// 券类型[-1-不可用 | 1-代金券｜2-满减券｜3-兑换券｜4-折扣券|101-优先券]
            case -1:
                voucher = BigDecimal.ZERO;
                break;
            case 1:
                if (totalPrice.compareTo(value) >= 0) {
                    voucher = value;
                } else {// 优惠金额大于了总价 优惠金额最大为总价
                    voucher = totalPrice;
                }
                break;
            default:// 其他类型如果使用成功了直接退掉 返回不可用
                type = -1;
                voucher = BigDecimal.ZERO;
                goblinOrderUtils.backCoupon(uCouponId, uid);
                break;
        }
        GoblinUseResultVo returnVo = GoblinUseResultVo.getNew();
        returnVo.setValue(voucher.setScale(2, BigDecimal.ROUND_HALF_UP));
        returnVo.setCouType(String.valueOf(type));
        return returnVo;

    }

    /**
     * 使用店铺优惠券
     *
     * @param uCouponId  券id
     * @param content    消费内容
     * @param totalPrice 订单总价
     * @param spuId      spuId
     * @param uid        uid
     * @return
     */
    public GoblinUseResultVo useStoreCoupon(String uCouponId, String content, BigDecimal totalPrice, String spuId, String uid) {
        List<GoblinUserCouponVo> voList = goblinRedisUtils.getUserCouponVos(uid);
        GoblinUseResultVo returnVo = GoblinUseResultVo.getNew();
        returnVo.setValue(BigDecimal.ZERO);
        returnVo.setCouType("-1");
        LocalDateTime now = LocalDateTime.now();

        for (GoblinUserCouponVo vo : voList) {
            if (vo.getUcouponId().equals(uCouponId) && vo.getDuedAt().isAfter(now) && vo.getState().equals(1) && vo.getUseScope().equals("1")) {// 判断可用
                List<String> couponSpuIds = goblinRedisUtils.getStoreCouponSpuIds(vo.getStoreCouponId());
                if (CollectionUtil.isEmpty(couponSpuIds)) {
                    break;
                }
                // 判断是否在可用商品内
                BigDecimal contentPrice = BigDecimal.ZERO;
                for (String item : couponSpuIds) {
                    if (spuId.equals(item)) {
                        contentPrice = totalPrice;
                    }
                }
                if (vo.getTriggers().compareTo(contentPrice) <= 0) {// 订单金额大于等于触发金额可使用
                    if (vo.getType().equals("1")) {//代金券
                        if (vo.getValFace().compareTo(contentPrice) > 0) {
                            returnVo.setValue(contentPrice);
                        } else {
                            returnVo.setValue(vo.getValFace());
                        }
                        returnVo.setCouType(vo.getType());
                    } else {
                        break;
                    }
                    // 更新券信息
                    vo.setState(5);
                    vo.setUsedFor(content);
                    goblinMongoUtils.changeCouponVos(vo.getUcouponId(), vo);
                    queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_COUPON.getKey(),
                            SqlMapping.get("goblin_user_coupon.updateState", vo.getState(), vo.getUsedFor(), LocalDateTime.now(), vo.getUcouponId()));
                }
                break;
            }
        }
        // 如果使用了更新redis
        if (!returnVo.getCouType().equals("-1")) {
            goblinRedisUtils.setUserCouponVos(uid, voList);
        }
        return returnVo;
    }

    //超时支付自动退款
    public Boolean refundOrderSku(String orderId, String paymentId, String paymentType, String payType) {
        LocalDateTime now = LocalDateTime.now();
        GoblinNftOrderVo nftOrder = getNftOrder(orderId);
        BigDecimal refundPrice = nftOrder.getPriceActual();
        //记录退款单
        String refundCode = IDGenerator.storeRefundCode(nftOrder.getOrderCode());
        GoblinNftOrderRefundVo backOrderVo = GoblinNftOrderRefundVo.getNew();
        backOrderVo.setOrderRefundId(IDGenerator.nextTimeId2());
        backOrderVo.setRefundCode(refundCode);
        backOrderVo.setOrderId(orderId);
        backOrderVo.setOrderCode(nftOrder.getOrderCode());
        backOrderVo.setStoreId(nftOrder.getStoreId());
        backOrderVo.setUserId(nftOrder.getUserId());
        backOrderVo.setPrice(refundPrice);
        backOrderVo.setStatus(GoblinStatusConst.NftStatus.ORDER_REFUND_STATUS_1.getValue());
        backOrderVo.setCreatedAt(now);
        backOrderVo.setErrorReason("");

        if (!payType.equals("applepay")) {// 苹果支付不做处理
            // 调用退款 超时支付回调可能还未完成 所以redisVo没有此俩个参数
            nftOrder.setPaymentId(paymentId);
            nftOrder.setPaymentType(paymentType);
            String returnString = initRefund(nftOrder, refundPrice, refundCode);
            HashMap hashMapResult = JsonUtils.fromJson(returnString, HashMap.class);
            Boolean success = (Boolean) hashMapResult.get("success");
            String message = (String) hashMapResult.get("message");
            if (!success) {
                if (!Objects.equals(backOrderVo.getStatus(), GoblinStatusConst.NftStatus.ORDER_REFUND_STATUS_2.getValue())) {
                    backOrderVo.setStatus(GoblinStatusConst.NftStatus.ORDER_REFUND_STATUS_3.getValue());
                    backOrderVo.setErrorReason(message);
                }
            }
        }

        /**
         * 退款表数据处理
         */
        setBackOrderVo(backOrderVo);// redis
        goblinMongoUtils.insertGoblinNftOrderRefundVo(backOrderVo);// mongo
        queueUtils.sendMsgByRedis(// mysql
                MQConst.GoblinQueue.GOBLIN_NFT_ORDER.getKey(),
                SqlMapping.get("goblin_nft_order_refund.insert",
                        backOrderVo.getOrderRefundId(), backOrderVo.getRefundCode(), backOrderVo.getOrderId(),
                        backOrderVo.getOrderCode(), backOrderVo.getStoreId(), backOrderVo.getUserId(),
                        backOrderVo.getPrice(), backOrderVo.getStatus(),
                        backOrderVo.getErrorReason(), now
                )
        );

        return true;
    }

    private String initRefund(GoblinNftOrderVo orderVo, BigDecimal price, String refundCode) {
        log.info("NFT退款请求 参数：[orderVo:{}, price:{}, refundCode:{}]", orderVo, price, refundCode);
        MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString();
        params.add("code", orderVo.getPayCode());
        params.add("notifyUrl", synUrl);
        params.add("orderCode", orderVo.getOrderCode());
        params.add("orderRefundCode", refundCode);
        params.add("paymentId", orderVo.getPaymentId());
        params.add("paymentType", orderVo.getPaymentType());
        params.add("price", String.valueOf(price));
        params.add("priceTotal", String.valueOf(price));
        params.add("reason", "NFT超时支付");
        MultiValueMap<String, String> headers = CollectionUtil.linkedMultiValueMapStringString();
        headers.add("Accept", "application/json;charset=UTF-8");
        String returnString = HttpUtil.post(refundApply, params, headers);
        log.info("NFT退款请求 结果：[returnString:{}]", returnString);
        return returnString;
    }

    public boolean isVipMember(String uid) {
        if (goblinRedisUtils.getMember(uid) != null) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *     private int skuType 商品类型[0-常规|1-数字藏品]
     *     private String status 审核状态[0-初始编辑|1-审核中|2-审核不通过|3-审核通过];
     *     private String shelvesStatus 单品上架状态[0-待上架|1-下架|2-违规|3-上架];
     *     private String skuAppear 是否隐藏[0-默认展示|1-隐藏]; 隐藏款 拼参数可购买 盲盒可抽
     *     private String delFlg 删除标记[0-未删除|1-删除];
     *
     *     private LocalDateTime saleStartTime 开售时间;
     *     private LocalDateTime saleStopTime 停售时间 预留 暂时不做处理;
     *     private String soldoutStatus 是否售罄[0-否|1-是];
     *
     *     private String skuCanbuy 是否购买[0-否|1-是] 这个用来预览 前端自己判断;
     *
     *     Integer upchain NFT上传声明状态[0-待上传|1-已声明|2-声明失败|9-声明中]
     *
     *     根据概率判断是否过滤当前sku 没设置概率或者设置了的返回true
     *
     *     String unbox; 是否盲盒[0-否|1-是]
     */
    // 获取盲盒下藏品的库存 各种状态下不能算库存的排除掉
    public int getSkuAllStatusStock(GoblinGoodsSkuInfoVo info) {
        if (
            info != null
            && LocalDateTime.now().isAfter(info.getSaleStartTime())
            && (null == info.getSoldoutStatus() || info.getSoldoutStatus().equals("0"))
            && (null == info.getSkuCanbuy() || info.getSkuCanbuy().equals("1"))
            && (null == info.getHitRatio() || info.getHitRatio().compareTo(BigDecimal.ZERO) > 0)
        ) {// 可以返回库存
            return goblinRedisUtils.getSkuStock(info.getSkuId());
        } else {// 不计入库存
            return 0;
        }
    }
    // 各种状态下判断藏品是否可以展示
    public boolean getSkuAllStatusShow(GoblinGoodsSkuInfoVo 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")
            && ((info.getUnbox().equals("0") && info.getUpchain() == 1) || info.getUnbox().equals("1"))
        ) {
            return true;
        } else {
            return false;
        }
    }

}
