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

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinBraceletOrder;
import com.liquidnet.service.goblin.enums.OrderStatus;
import com.liquidnet.service.goblin.enums.PayStatus;
import com.liquidnet.service.goblin.mapper.GoblinBraceletOrderMapper;
import com.liquidnet.service.goblin.param.GoblinBraceletOrderPayParam;
import com.liquidnet.service.goblin.param.dougong.DougongJsPayData;
import com.liquidnet.service.goblin.param.dougong.DougongRequestParam;
import com.liquidnet.service.goblin.param.dougong.DougongSyncCallbackparam;
import com.liquidnet.service.goblin.service.IGoblinBraceletOrderService;
import com.liquidnet.service.goblin.service.IGoblinBraceletOrderService;
import com.liquidnet.service.goblin.service.IGoblinDougongPayService;
import com.liquidnet.service.goblin.service.IGoblinRechargeWristbandService;
import com.liquidnet.service.goblin.util.GoblinRedisUtils;
import com.liquidnet.service.goblin.util.QueueUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GoblinBraceletOrderServiceImpl extends ServiceImpl<GoblinBraceletOrderMapper, GoblinBraceletOrder> implements IGoblinBraceletOrderService {

    @Autowired
    GoblinRedisUtils goblinRedisUtils;
    @Autowired
    private QueueUtils queueUtils;
    @Autowired
    private IGoblinRechargeWristbandService rechargeWristbandService;
    @Autowired
    private IGoblinDougongPayService dougongPayService;


    @Override
    public ResponseDto<GoblinBraceletPayResultVo> checkOrder(GoblinBraceletOrderPayParam payParam) {
        log.info("request param: {}.", JsonUtils.toJson(payParam));
        // 1. 检查商品
        GoblinRechargeWristbandVo rechargeWristbandVo = rechargeWristbandService.getList();
        if (rechargeWristbandVo == null) {
            log.error("查询商品为空");
            return ResponseDto.failure("下单失败");
        }
        // 2. 计算金额
        if (!rechargeWristbandVo.getWristbandId().equals(payParam.getWristbandId())) {
            log.error("下单商品不存在");
            return ResponseDto.failure("下单商品不存在");
        }

        List<GoblinRechargeAmountVo> amonutList = rechargeWristbandVo.getAmonutList();
        if (amonutList.isEmpty()) {
            log.error("充值面额不存在");
            return ResponseDto.failure("充值面额不存在");
        }

        List<GoblinRechargeAmountVo> payAmountList = amonutList.stream()
                .filter(a -> a.getAmountId().equals(payParam.getAmountId()))
                .collect(Collectors.toList());

        if (payAmountList.isEmpty()) {
            log.error("充值面额不存在");
            return ResponseDto.failure("充值面额不存在");
        }

        // 要充值的面额
        final GoblinRechargeAmountVo payAmountVo = payAmountList.get(0);

        final GoblinBraceletOrderVo orderVo = getOrderVo(rechargeWristbandVo.getWristbandId(),
                rechargeWristbandVo.getName(),
                rechargeWristbandVo.getPrice(),
                payAmountVo, payParam);

        if (orderVo == null) {
            log.error("下单失败");
            return ResponseDto.failure("下单失败");
        }

        // 4. 调用斗拱拉起支付
        DougongJsPayData dougongJsPayData = new DougongJsPayData();
        dougongJsPayData.setReqDate(orderVo.getReqDate());
        dougongJsPayData.setReqSeqId(orderVo.getReqSeqId());
        dougongJsPayData.setTradeType(orderVo.getTradeType());
        dougongJsPayData.setGoodsDesc(orderVo.getGoodsDesc());
        dougongJsPayData.setTransAmt(orderVo.getPriceTotal().setScale(2, RoundingMode.HALF_UP).toPlainString());
        dougongJsPayData.setTimeExpire(DateUtil.format(DateUtil.addHour(new Date(), 2), DateUtil.Formatter.yyyyMMddHHmmssTrim));
        dougongJsPayData.setSubOpenid(payParam.getSubOpenId());
        dougongJsPayData.setGoodsDetailList(getGoodsDetailList(orderVo.getOrderEntitiesDetailVoList()));

        Map<String, Object> dougongResponseMap = dougongPayService.jsPay(dougongJsPayData);
        if (null == dougongResponseMap || dougongResponseMap.isEmpty()) {
            log.error("拉起下单失败");
            return ResponseDto.failure("下单失败");
        }

        // 根据响应接口填充参数
        orderVo.setHfSeqId((String) dougongResponseMap.get("hf_seq_id"));
        orderVo.setAtuSubMerId((String) dougongResponseMap.get("atu_sub_mer_id"));
        orderVo.setPartyOrderId((String) dougongResponseMap.get("party_order_id"));

        // 5. 根据响应结构写 redis、mysql
        goblinRedisUtils.setBraceletOrderVo(orderVo);
        goblinRedisUtils.setBraceletRelatedOrderVo(orderVo.getReqSeqId(), orderVo.getOrderId());

        LinkedList<String> sqls = CollectionUtil.linkedListString();
        sqls.add(SqlMapping.get("goblin_bracelet_order_insert"));
        sqls.add(SqlMapping.get("goblin_bracelet_order_entities_insert"));
        LinkedList<Object[]> sqlDataOrder = CollectionUtil.linkedListObjectArr();


        sqlDataOrder.add(new Object[]{
                orderVo.getOrderId(), orderVo.getUserId(), orderVo.getBindName(), orderVo.getBindMobile(), orderVo.getBindIdcard(), orderVo.getReqDate(), orderVo.getGoodsDesc(),
                orderVo.getReqSeqId(), orderVo.getHfSeqId(), orderVo.getTradeType(), orderVo.getAtuSubMerId(), orderVo.getPartyOrderId(),
                orderVo.getPrice(), orderVo.getPriceTotal(), orderVo.getPriceRefund(), orderVo.getRefundPriceCharges(),
                orderVo.getRefundNumber(), orderVo.getStatus(), orderVo.getPayStatus(), orderVo.getCreatedAt(), orderVo.getUpdatedAt()
        });
        LinkedList<Object[]> sqlDataOrderEntities = CollectionUtil.linkedListObjectArr();
        for (GoblinBraceletOrderEntitiesVo entitiesVo : orderVo.getOrderEntitiesDetailVoList()) {
            sqlDataOrderEntities.add(new Object[]{
                    entitiesVo.getOrderEntitiesId(), entitiesVo.getOrderId(), entitiesVo.getGoodsType(), entitiesVo.getGoodsId(), entitiesVo.getGoodsName(),
                    entitiesVo.getGoodsPrice(), entitiesVo.getStatus(), entitiesVo.getPayStatus(), entitiesVo.getCreatedAt(),
                    entitiesVo.getUpdatedAt()
            });
        }

        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.SQL_GOODS.getKey(),
                SqlMapping.gets(sqls, sqlDataOrder, sqlDataOrderEntities)
        );

        // 6. 响应数据
        GoblinBraceletPayResultVo resultVo = GoblinBraceletPayResultVo.getNew();
        resultVo.setOrderId(orderVo.getOrderId());
        resultVo.setPrice(orderVo.getPriceTotal());
        resultVo.setPayInfo(dougongResponseMap.get("pay_info"));
        return ResponseDto.success(resultVo);
    }

    private List<DougongRequestParam.WxDataGoodsDetail> getGoodsDetailList(List<GoblinBraceletOrderEntitiesVo> orderEntitiesDetailVoList) {
        if (orderEntitiesDetailVoList.isEmpty()) {
            return Collections.emptyList();
        }
        List<DougongRequestParam.WxDataGoodsDetail> goodsDetailList = new ArrayList<>(orderEntitiesDetailVoList.size());
        for (GoblinBraceletOrderEntitiesVo entitiesVo : orderEntitiesDetailVoList) {
            DougongRequestParam.WxDataGoodsDetail detail = new DougongRequestParam.WxDataGoodsDetail();
            detail.setGoodsId(entitiesVo.getGoodsId());
            detail.setGoodsName(entitiesVo.getGoodsName());
            detail.setPrice(entitiesVo.getGoodsPrice().toPlainString());
            detail.setQuantity(1);
            goodsDetailList.add(detail);
        }
        return goodsDetailList;
    }

    /**
     * @param wristbandId 手环ID
     * @param name        手环名称
     * @param price       手环价格
     * @param payAmountVo 充值面额VO
     * @param payParam    请求参数
     * @return orderId
     */
    private GoblinBraceletOrderVo getOrderVo(String wristbandId,
                                             String name,
                                             BigDecimal price,
                                             GoblinRechargeAmountVo payAmountVo,
                                             GoblinBraceletOrderPayParam payParam) {
        // 需付款金额
        BigDecimal totalAmount = BigDecimal.ZERO.add(price).add(payAmountVo.getPrice());

        final String uid = CurrentUtil.getCurrentUid();
        log.info("用户下单, 需要支付金额: {}, uid: {}.", totalAmount.toPlainString(), uid);

        final String orderId = IDGenerator.nextSnowId();

        try {
            List<GoblinBraceletOrderEntitiesVo> list = new ArrayList<>();
            list.add(buildRechargeOrderEntitiesVo(wristbandId, name, price, 1, orderId));
            list.add(buildRechargeOrderEntitiesVo(payAmountVo.getAmountId(), payAmountVo.getName(), payAmountVo.getPrice(), 2, orderId));

            GoblinBraceletOrderVo orderVo = buildRechargeOrder(payParam, orderId, uid, totalAmount);
            orderVo.setOrderEntitiesDetailVoList(list);
            return orderVo;
        } catch (Exception e) {
            log.error("error", e);
            return null;
        }
    }

    private GoblinBraceletOrderVo buildRechargeOrder(GoblinBraceletOrderPayParam payParam, String orderId, String uid, BigDecimal totalAmount) {
        GoblinBraceletOrderVo braceletOrderVo = new GoblinBraceletOrderVo();
        braceletOrderVo.setOrderId(orderId);
        braceletOrderVo.setUserId(uid);
        braceletOrderVo.setBindName(payParam.getName());
        braceletOrderVo.setBindMobile(payParam.getMobile());
        braceletOrderVo.setBindIdcard(payParam.getIdCard());
        braceletOrderVo.setReqDate(DateUtil.getNowTime("yyyyMMdd"));
        braceletOrderVo.setGoodsDesc(getGoodsDesc());
        braceletOrderVo.setReqSeqId(IDGenerator.nextSnowId());
        braceletOrderVo.setTradeType("T_MINIAPP");
        braceletOrderVo.setPrice(totalAmount);
        braceletOrderVo.setPriceTotal(totalAmount);
        braceletOrderVo.setPriceRefund(BigDecimal.ZERO);
        braceletOrderVo.setRefundPriceCharges(BigDecimal.ZERO);
        braceletOrderVo.setRefundNumber(0);
        braceletOrderVo.setStatus(OrderStatus.PENDING_PAYMENT.getCode());
        braceletOrderVo.setPayStatus(PayStatus.NOT_PAID.getCode());
        braceletOrderVo.setCreatedAt(LocalDateTime.now());
        braceletOrderVo.setUpdatedAt(LocalDateTime.now());
        return braceletOrderVo;
    }

    private GoblinBraceletOrderEntitiesVo buildRechargeOrderEntitiesVo(String goodsId, String goodsName, BigDecimal goodsPrice, Integer goodsType, String orderId) {
        GoblinBraceletOrderEntitiesVo entitiesVo = new GoblinBraceletOrderEntitiesVo();
        entitiesVo.setOrderId(orderId);
        entitiesVo.setOrderEntitiesId(IDGenerator.nextSnowId());
        entitiesVo.setGoodsType(goodsType);
        entitiesVo.setGoodsId(goodsId);
        entitiesVo.setGoodsName(goodsName);
        entitiesVo.setGoodsPrice(goodsPrice);
        entitiesVo.setStatus(OrderStatus.PENDING_PAYMENT.getCode());
        entitiesVo.setPayStatus(PayStatus.NOT_PAID.getCode());
        entitiesVo.setCreatedAt(LocalDateTime.now());
        entitiesVo.setUpdatedAt(LocalDateTime.now());
        return entitiesVo;
    }

    /**
     * 获取商品描述
     *
     * @return
     */
    private String getGoodsDesc() {
        return "手环充值";
    }

    @Override
    public ResponseDto<Integer> checkOrderResult(String orderId) {
        final String uid = CurrentUtil.getCurrentUid();
        GoblinBraceletOrderVo braceletOrderVo = goblinRedisUtils.getBraceletOrderVo(orderId);
        if (braceletOrderVo == null) {
            log.error("订单不存在");
            return ResponseDto.failure("订单不存在");
        }
        if (!uid.equals(braceletOrderVo.getUserId())) {
            log.error("暂无权限");
            return ResponseDto.failure("暂无权限");
        }
        return ResponseDto.success(braceletOrderVo.getPayStatus());
    }

    @Override
    public ResponseDto<Boolean> dougongCallBack(DougongSyncCallbackparam callbackparam) {
        log.info("斗拱回调参数: {}.", JsonUtils.toJson(callbackparam));
        // 验签
        boolean verifySign = dougongPayService.verifySign(callbackparam.getRespData(), callbackparam.getSign());
        // 2. 判断验签结果
        if (!verifySign) {
            log.error("斗拱回调验签失败");
            return ResponseDto.failure();
        }
        try {
            Map<String, Object> callbackRespDataMap = JsonUtils.fromJson(callbackparam.getRespData(), Map.class);
            if (callbackRespDataMap == null || !"00000000".equals(callbackRespDataMap.get("resp_code"))) {
                log.error("斗拱回调数据为空");
                return ResponseDto.failure();
            }

            final String reqSeqId = (String) callbackRespDataMap.get("req_seq_id");
            final String orderId = goblinRedisUtils.getBraceletRelatedOrderVo(reqSeqId);

            if (StringUtil.isBlank(orderId)) {
                log.error("未找到对应的订单, reqSeqId: {}.", reqSeqId);
                return ResponseDto.failure();
            }
            GoblinBraceletOrderVo orderVo = goblinRedisUtils.getBraceletOrderVo(orderId);
            if (orderVo == null) {
                log.error("未找到对应的订单, reqSeqId: {}.", reqSeqId);
                return ResponseDto.failure();
            }

            orderVo.setOutTransId((String) callbackRespDataMap.get("out_trans_id"));
            orderVo.setEndTime((String) callbackRespDataMap.get("end_time"));
            orderVo.setAcctDate((String) callbackRespDataMap.get("acct_date"));
            orderVo.setPriceActual(new BigDecimal((String) callbackRespDataMap.get("pay_amt")));
            orderVo.setTimePay((String) callbackRespDataMap.get("end_time"));
            orderVo.setUpdatedAt(LocalDateTime.now());

            if ("S".equalsIgnoreCase((String) callbackRespDataMap.get("trans_stat"))) {
                // 斗拱返回成功
                updateOrderStatus(OrderStatus.PAID.getCode(), PayStatus.PAID.getCode(), orderVo);
            } else {
                // 斗拱返回失败
                updateOrderStatus(OrderStatus.PENDING_PAYMENT.getCode(), PayStatus.PAYMENT_FAILED.getCode(), orderVo);
            }

            // 更新redis
            goblinRedisUtils.setBraceletOrderVo(orderVo);

            // 更新MySQL
            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(),
                    SqlMapping.get("gpblin_bracelet_order_update", orderVo.getOutTransId(), orderVo.getEndTime(),
                            orderVo.getAcctDate(), orderVo.getPriceActual(), orderVo.getTimePay(), orderVo.getUpdatedAt(), orderVo.getOrderId())
            );

            for (GoblinBraceletOrderEntitiesVo entitiesVo : orderVo.getOrderEntitiesDetailVoList()) {
                queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(),
                        SqlMapping.get("goblie_bracelet_order_entities_update", entitiesVo.getStatus(),
                                entitiesVo.getPayStatus(), entitiesVo.getUpdatedAt(), entitiesVo.getOrderEntitiesId())
                );
            }
            return ResponseDto.success(Boolean.TRUE);
        } catch (Exception e) {
            log.error("error", e);
            return ResponseDto.failure();
        }

    }

    private void updateOrderStatus(int statusCode, int payStatusCode, GoblinBraceletOrderVo orderVo) {
        orderVo.setStatus(statusCode);
        orderVo.setPayStatus(payStatusCode);
        List<GoblinBraceletOrderEntitiesVo> orderEntitiesDetailVoList = orderVo.getOrderEntitiesDetailVoList();
        for (GoblinBraceletOrderEntitiesVo entitiesVo : orderEntitiesDetailVoList) {
            entitiesVo.setStatus(statusCode);
            entitiesVo.setPayStatus(payStatusCode);
            entitiesVo.setUpdatedAt(LocalDateTime.now());
        }
    }
}
