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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.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;
    @Autowired
    private IGoblinRechargeWristbandService iGoblinRechargeWristbandService;
    @Autowired
    private GoblinBraceletOrderMapper braceletOrderMapper;


    @Override
    public ResponseDto<GoblinBraceletPayResultVo> checkOrder(GoblinBraceletOrderPayParam payParam) {
        log.info("request param: {}.", JsonUtils.toJson(payParam));
        LambdaQueryWrapper<GoblinBraceletOrder> queryWrapper = new QueryWrapper<GoblinBraceletOrder>()
                .lambda()
                .eq(GoblinBraceletOrder::getBindIdcard, payParam.getIdCard())
                .in(GoblinBraceletOrder::getStatus, OrderStatus.PAID.getCode(), OrderStatus.REFUNDING.getCode(),
                        OrderStatus.PARTIAL_REFUND.getCode());
        List<GoblinBraceletOrder> goblinBraceletOrders = braceletOrderMapper.selectList(queryWrapper);
        if (!goblinBraceletOrders.isEmpty()) {
            log.info("不能重复购买");
            return ResponseDto.failure("不能重复购买");
        }

        GoblinRechargeWristbandVo rechargeWristbandVo = rechargeWristbandService.getList();
        if (rechargeWristbandVo == null) {
            log.error("查询商品为空");
            return ResponseDto.failure("下单失败");
        }
        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 String orderId = IDGenerator.nextSnowId();
        final GoblinBraceletOrderVo orderVo = buildRechargeOrder(orderId, rechargeWristbandVo.getWristbandId(), rechargeWristbandVo.getPrice(),
                payAmountVo.getAmountId(), payAmountVo.getPrice(), 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(rechargeWristbandVo.getWristbandId(),
                rechargeWristbandVo.getPrice(), rechargeWristbandVo.getName(), payAmountVo));

        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.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"));
        LinkedList<Object[]> sqlDataOrder = CollectionUtil.linkedListObjectArr();

        sqlDataOrder.add(new Object[]{
                orderVo.getOrderId(), orderVo.getUserId(), orderVo.getBindName(), orderVo.getBindMobile(), orderVo.getBindIdcard(),
                orderVo.getReqDate(), orderVo.getGoodsDesc(), orderVo.getWristbandId(), orderVo.getWristbandPrice(), orderVo.getAmountId(),
                orderVo.getAmountPrice(), orderVo.getReqSeqId(), orderVo.getHfSeqId(), orderVo.getTradeType(),
                orderVo.getPartyOrderId(), orderVo.getPrice(), orderVo.getPriceTotal(), orderVo.getPriceRefund(),
                orderVo.getRefundPriceCharges(), orderVo.getRefundNumber(), orderVo.getStatus(), orderVo.getPayStatus(),
                orderVo.getCreatedAt(), orderVo.getUpdatedAt()
        });

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

        // 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(String wristbandId,
                                                                           BigDecimal wristbandPrice,
                                                                           String wristbandName,
                                                                           GoblinRechargeAmountVo payAmountVo) {
        List<DougongRequestParam.WxDataGoodsDetail> goodsDetailList = new ArrayList<>();
        goodsDetailList.add(new DougongRequestParam.WxDataGoodsDetail(wristbandId, wristbandName, wristbandPrice.toPlainString(), 1));
        goodsDetailList.add(new DougongRequestParam.WxDataGoodsDetail(payAmountVo.getAmountId(), payAmountVo.getName(), payAmountVo.getPrice().toPlainString(), 1));
        return goodsDetailList;
    }


    /**
     * @param orderId        手环订单ID
     * @param wristbandId    手环ID
     * @param wristbandPrice 手环金额
     * @param amountId       充值金额ID
     * @param amountPrice    充值金额
     * @param payParam
     * @return
     */
    private GoblinBraceletOrderVo buildRechargeOrder(String orderId,
                                                     String wristbandId,
                                                     BigDecimal wristbandPrice,
                                                     String amountId,
                                                     BigDecimal amountPrice,
                                                     GoblinBraceletOrderPayParam payParam) {
        BigDecimal totalAmount = BigDecimal.ZERO.add(wristbandPrice).add(amountPrice);
        final String uid = CurrentUtil.getCurrentUid();
        log.info("用户{}需要付款金额: {}.", uid, totalAmount.toPlainString());

        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.setWristbandId(wristbandId);
        braceletOrderVo.setWristbandPrice(wristbandPrice);
        braceletOrderVo.setAmountId(amountId);
        braceletOrderVo.setAmountPrice(amountPrice);
        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;
    }


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

    @Override
    public ResponseDto<Map<String, Object>> 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("暂无权限");
        }
        Map<String, Object> map = new HashMap<>();
        map.put("status", braceletOrderVo.getPayStatus());
        map.put("outno", braceletOrderVo.getFoutTradeNo());
        return ResponseDto.success(map);
    }

    @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.getOrDefault("acct_date", callbackRespDataMap.get("end_time")));
            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"))) {
                // 斗拱返回成功
                orderVo.setStatus(OrderStatus.PAID.getCode());
                orderVo.setPayStatus(PayStatus.PAID.getCode());

                // 去迈之下单
                MaiZhiAllVo.OrderParam orderParam = MaiZhiAllVo.OrderParam.getNew();
                orderParam.setOperationNo("2");
                orderParam.setOut_trade_no(orderVo.getPartyOrderId());
                orderParam.setDgoid(orderVo.getHfSeqId());
                orderParam.setReceipt_amount(orderVo.getPriceTotal());
                orderParam.setTotal(1);
                orderParam.setPayTime(orderVo.getEndTime());
                List<MaiZhiAllVo.OrderParam.User> userList = new ArrayList<>();
                MaiZhiAllVo.OrderParam.User user = MaiZhiAllVo.OrderParam.User.getNew();
                user.setIdcard(orderVo.getBindIdcard());
                user.setName(orderVo.getBindName());
                user.setPhone(orderVo.getBindMobile());
                user.setMoney(orderVo.getAmountPrice());
                userList.add(user);
                orderParam.setUserList(userList);
                iGoblinRechargeWristbandService.createOrder(orderParam, orderId);
            } else {
                // 斗拱返回失败
//                orderVo.setStatus(OrderStatus.PAID.getCode());
                orderVo.setPayStatus(PayStatus.PAYMENT_FAILED.getCode());
            }

            // 更新redis
            GoblinBraceletOrderVo orderVo1 = goblinRedisUtils.getBraceletOrderVo(orderId);
            orderVo.setFoutTradeNo(orderVo1.getFoutTradeNo());
            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.getStatus(),
                            orderVo.getPayStatus(), orderVo.getUpdatedAt(), orderVo.getOrderId())
            );
            return ResponseDto.success(Boolean.TRUE);
        } catch (Exception e) {
            log.error("error", e);
            return ResponseDto.failure();
        }

    }

    @Override
    public List<GoblinBraceletOrderVo> getOrderList() {
        final String uid = CurrentUtil.getCurrentUid();
        LambdaQueryWrapper<GoblinBraceletOrder> queryWrapper = new QueryWrapper<GoblinBraceletOrder>()
                .lambda()
                .eq(GoblinBraceletOrder::getUserId, uid)
                .in(GoblinBraceletOrder::getStatus, OrderStatus.PAID.getCode(), OrderStatus.REFUNDING.getCode(),
                        OrderStatus.REFUNDED.getCode())
                .orderByDesc(GoblinBraceletOrder::getCreatedAt);
        List<GoblinBraceletOrder> goblinBraceletOrders = braceletOrderMapper.selectList(queryWrapper);
        if (goblinBraceletOrders.isEmpty()) {
            return Collections.emptyList();
        }
        return goblinBraceletOrders.stream()
                .map(m -> GoblinBraceletOrderVo.getNew().copy(m))
                .collect(Collectors.toList());
    }

}
