package com.xxfc.platform.universal.handler;

import com.alibaba.fastjson.JSON;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.xxfc.platform.universal.biz.OrderPayBiz;
import com.xxfc.platform.universal.constant.AliPayErrorEnum;
import com.xxfc.platform.universal.constant.PayWay;
import com.xxfc.platform.universal.constant.WxPayErrorEnum;
import com.xxfc.platform.universal.vo.FundPayVo;
import com.xxfc.platform.universal.constant.AliNeedPayErrorEnum;
import com.xxfc.platform.universal.constant.WxNeedPayErrorEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.xxfc.platform.universal.config.RabbitUniversalConfig.WITH_DRAW_QUEUE;

/**
 * @author libin
 * @version 1.0
 * @description
 * @data 2019/10/17 17:20
 */
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class WithDrawMqHandler implements InitializingBean {
    private List<String> needPayErrorCodes;
    private List<String> wxPayErrorCodes;
    private List<String> aliPayErrorCodes;
    private final UserFeign userFeign;
    private final OrderPayBiz orderPayBiz;
    @Resource(name = "redisTemplate")
    private ValueOperations valueOperations;
    private final RedisTemplate<String, String> redisTemplate;
    private static final int MAX_RETRY = 3;
    private static final String DEFAULT_ERROR_MSG = "当前操作有误，如有疑问请咨询客服";

    /**
     * 钱包提现
     *
     * @param
     */
    @RabbitListener(queues = WITH_DRAW_QUEUE)
    public void integralHandler(Message message, @Headers Map<String, Object> headers, Channel channel) {
        FundPayVo fundPayVo = null;
        String pay_way = "";
        Integer cathType = null;
        String orderNo = "";
        try {
            String msg = new String(message.getBody(), "UTF-8");
            fundPayVo = JSON.parseObject(msg, FundPayVo.class);
            cathType = fundPayVo.getType();
            pay_way = fundPayVo.getType() == PayWay.WX_PAY.getCode() ?
                    PayWay.WX_PAY.getDesc() : fundPayVo.getType() == PayWay.ALI_PAY.getCode() ?
                    PayWay.ALI_PAY.getDesc() : fundPayVo.getType() == PayWay.BANK.getCode() ?
                    PayWay.BANK.getDesc() : "";
            String cono = orderPayBiz.fundTrans(fundPayVo);
            orderNo = fundPayVo.getOrderNo();
            log.info("提现成功：【商户订单号：{}】-->【{}】", cono,msg);
            userFeign.withDrawprocess(fundPayVo.getOrderNo(), cono, "", true);
            Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
            // 手动签收
            basicAck(channel, deliveryTag, false, pay_way);
        } catch (UnsupportedEncodingException e) {
            basicNack(channel, message.getMessageProperties().getDeliveryTag(), false, false, pay_way);
            userFeign.withDrawprocess(fundPayVo.getOrderNo(), "",DEFAULT_ERROR_MSG, false);
            String reason = String.format("%s%s%s", pay_way, "转账失败", "[不支持的编码:UTF-8]");
            log.info("{}:【{}】", reason, e);
        } catch (BaseException e) {
            //错误码
            String subCode = e.getSubCode();
            //再次发消息
            if (needPayErrorCodes.contains(subCode)) {
                Long count = valueOperations.increment(orderNo);
                if (count <= MAX_RETRY) {
                    //重新把消息放回队列
                    basicNack(channel, message.getMessageProperties().getDeliveryTag(), false, true, pay_way);
                    return;
                }
                redisTemplate.delete(orderNo);
            }
            String msg = "";
            if (wxPayErrorCodes.contains(subCode) && cathType != null && cathType == PayWay.WX_PAY.getCode()) {
                msg = WxPayErrorEnum.valueOf(subCode).getSubReason();
            }
            if (aliPayErrorCodes.contains(subCode) && cathType != null && cathType == PayWay.ALI_PAY.getCode()) {
                msg = AliPayErrorEnum.valueOf(subCode).getSubReason();
            }
            msg = StringUtils.isEmpty(msg) ? DEFAULT_ERROR_MSG : msg;

            userFeign.withDrawprocess(fundPayVo.getOrderNo(), "", msg, false);
            //放弃此消息
            basicNack(channel, message.getMessageProperties().getDeliveryTag(), false, false, pay_way);
        }
    }

    private void basicAck(Channel channel, long deliveryTag, boolean multiple, String pay_way) {
        try {
            channel.basicAck(deliveryTag, multiple);
        } catch (IOException ex) {
            log.info("{}提现转账消息签收失败:【{}】", pay_way, ex);
        }
    }

    private void basicNack(Channel channel, long deliveryTag, boolean multiple, boolean requeue, String pay_way) {
        try {
            channel.basicNack(deliveryTag, multiple, requeue);
        } catch (IOException ex) {
            log.info("{}提现转账消息{}失败:【{}】", pay_way, requeue ? "重回队列" : "丢弃", ex);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        needPayErrorCodes = Stream.concat(EnumSet.allOf(WxNeedPayErrorEnum.class).stream().map(WxNeedPayErrorEnum::name), EnumSet.allOf(AliNeedPayErrorEnum.class).stream().map(AliNeedPayErrorEnum::name))
                .distinct()
                .collect(Collectors.toList());
        wxPayErrorCodes = EnumSet.allOf(WxPayErrorEnum.class).stream().map(WxPayErrorEnum::name).collect(Collectors.toList());
        aliPayErrorCodes = EnumSet.allOf(AliPayErrorEnum.class).stream().map(AliPayErrorEnum::name).collect(Collectors.toList());
    }
}
