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.GoblinNftOrderCallBackVo;
import com.liquidnet.service.goblin.dto.vo.GoblinNftOrderVo;
import com.liquidnet.service.goblin.dto.vo.GoblinUseResultVo;
import com.liquidnet.service.goblin.dto.vo.GoblinUserCouponVo;
import com.liquidnet.service.goblin.entity.GoblinNftOrderRefund;
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 skuId, int number) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_BUY_COUNT.concat(uid + ":skuId:" + skuId);
        long decr = redisUtil.decr(redisKey, number);
        String redisKey2 = GoblinRedisConst.REAL_STOCK_SKU.concat(skuId);
        long incr = redisUtil.incr(redisKey2, 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;
        }
    }

    // 添加 订单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(GoblinNftOrderCallBackVo vo) {
        String redisKey = GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_REFUND_INFO.concat(vo.getOrderId());
        redisUtil.set(redisKey, vo);
    }

    /**
     * 使用平台优惠券 只支持代金券
     *
     * @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) {
        LocalDateTime now = LocalDateTime.now();
        GoblinNftOrderVo nftOrder = getNftOrder(orderId);
        BigDecimal refundPrice = nftOrder.getPriceActual();
        //记录退款单
        String refundCode = IDGenerator.storeRefundCode(nftOrder.getOrderCode());
        GoblinNftOrderRefund nftBackOrder = GoblinNftOrderRefund.getNew();
        nftBackOrder.setOrderRefundId(IDGenerator.nextTimeId2());
        nftBackOrder.setRefundCode(refundCode);
        nftBackOrder.setOrderId(orderId);
        nftBackOrder.setOrderCode(nftOrder.getOrderCode());
        nftBackOrder.setStoreId(nftOrder.getStoreId());
        nftBackOrder.setUserId(nftOrder.getUserId());
        nftBackOrder.setSkuIdNums(nftOrder.getSkuId());
        nftBackOrder.setRealBackPrice(refundPrice);
        nftBackOrder.setStatus(GoblinStatusConst.NftStatus.ORDER_REFUND_STATUS_1.getValue());
        nftBackOrder.setCreatedAt(now);
        nftBackOrder.setUpdatedAt(now);
        nftBackOrder.setRefundAt(LocalDateTime.now());
        nftBackOrder.setErrorReason("");

        GoblinNftOrderCallBackVo backOrderVo = GoblinNftOrderCallBackVo.getNew().copy(nftBackOrder);

        // 调用退款
        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_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_nft_order_refund.insert",
                        nftBackOrder.getOrderRefundId(), nftBackOrder.getRefundCode(), nftBackOrder.getOrderId(),
                        nftBackOrder.getOrderCode(), nftBackOrder.getStoreId(), nftBackOrder.getUserId(),
                        nftBackOrder.getSkuIdNums(), nftBackOrder.getRealBackPrice(), backOrderVo.getStatus(),
                        nftBackOrder.getRefundAt(), backOrderVo.getErrorReason()
                )
        );
        if (success) {
            return true;
        } else {
            return false;
        }
    }

    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;
    }

}
