package com.izouma.nineth.service.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayResponse;
import com.alipay.api.internal.util.AlipaySignature;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.service.WxPayService;
import com.izouma.nineth.config.AlipayProperties;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.config.WxPayProperties;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.BankValidate;
import com.izouma.nineth.domain.UserBankCard;
import com.izouma.nineth.dto.pay.*;
import com.izouma.nineth.enums.*;
import com.izouma.nineth.event.GiftOrderNotifyEvent;
import com.izouma.nineth.event.OrderNotifyEvent;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.service.*;
import com.izouma.nineth.service.sms.SmsService;
import com.izouma.nineth.utils.BankUtils;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Objects;

@Service(UniformPayConstants.CUSTOM_PLATFORM)
@AllArgsConstructor
@Slf4j
public class CustomPlatformImpl implements UniformPayService{

    private GeneralProperties generalProperties;

    private OrderRepo orderRepo;

    private GiftOrderRepo giftOrderRepo;

    private SnowflakeIdWorker snowflakeIdWorker;

    private PaymentInfoRepo paymentInfoRepo;

    private UserRepo userRepo;

    private IdentityAuthRepo identityAuthRepo;

    private UserBankCardRepo userBankCardRepo;

    private SmsService smsService;

    private CollectionRepo collectionRepo;

    private DivMoneyService divMoneyService;

    private PaymentInfoService paymentInfoService;

    private WxPayProperties wxPayProperties;
    private WeixinService weixinService;
    private WxPayService wxPayService;

    private AlipayProperties alipayProperties;
    private AlipayService alipayService;

    private PayHelper payHelper;

    private RocketMQTemplate rocketMQTemplate;

    private ApplicationContext context;

    @Cacheable(value = UniformPayConstants.CUSTOM_PLATFORM, key = "#request.uniqueId")
    @Override
    public UniformPayResult pay(UniformPayRequest request) throws Exception {
        UniformPayResult.UniformPayResultBuilder resultBuilder = UniformPayResult.builder();
        //通过交易类型，判断是支付宝还是微信
        TradeType tradeType = request.getTradeType();
        boolean isAlipay = false;
        String payChannel = null;
        switch(tradeType){
            case ALIPAY_WAP:
            case ALIPAY_APP:
            case ALIPAY_QR:
            case QUICK_PAY:
                isAlipay = true;
                payChannel = "alipay_wap";
                break;
            case WX_PUB:
                payChannel = "wx_pub";
                break;
            case WX_LITE:
                payChannel = "wx_lite";
                break;
            default:
                throw new BusinessException("不支持此渠道");
        }

        PayMethod payMethod = payChannel.startsWith("wx") ? PayMethod.WEIXIN : PayMethod.ALIPAY;

        LocalDateTime expireTime = null;
        String orderNo = String.valueOf(snowflakeIdWorker.nextId());
        String notifyUrl = payHelper.resolvePayNotifyUrl(orderNo);
        //本地保存支付信息
        PaymentInfo paymentInfo = PaymentInfo.builder().orderNo(orderNo)
                .payMethod(payMethod)
                .payChannel(payChannel)
                .status(PayStatus.UNPAID)
                .merchantName(null)
                .notifyUrl(notifyUrl)
                .payPlatform(UniformPayConstants.CUSTOM_PLATFORM).build();
        String clientIp = null;
        if(request.getOrderId() != null){
            Long orderId = request.getOrderId();
            Order order = orderRepo.findByIdAndDelFalse(orderId).orElseThrow(new BusinessException("订单不存在"));
            expireTime = payHelper.checkExpireTime(order.getCreatedAt());
            //不支持转让交易
            Collection collection = collectionRepo.findById(order.getCollectionId())
                    .orElseThrow(new BusinessException("藏品不存在"));
//            if (collection.getSource() == CollectionSource.TRANSFER) {
//                throw new BusinessException("当前支付平台不支持转让交易");
//            }

            BigDecimal payAmt = order.getTotalPrice().setScale(2, RoundingMode.HALF_UP);
            String goodsTitle = order.getName();

            paymentInfo.setUserId(order.getUserId());
            paymentInfo.setPayAmt(payAmt);
            paymentInfo.setGoodsTitle(goodsTitle);
            paymentInfo.setOrderId(order.getId());
            paymentInfo.setProjectId(order.getProjectId());
            //开始记录分账
            divMoneyService.saveDivMoney(order, collection, UniformPayConstants.CUSTOM_PLATFORM);

            clientIp = order.getIp();
        }else{
            Long giftOrderId = request.getGiftOrderId();
            GiftOrder order = giftOrderRepo.findById(giftOrderId).orElseThrow(new BusinessException("转赠订单不存在"));
            expireTime = payHelper.checkExpireTime(order.getCreatedAt());
            BigDecimal payAmt = order.getGasPrice().setScale(2, RoundingMode.HALF_UP);
            paymentInfo.setUserId(order.getUserId());
            paymentInfo.setPayAmt(payAmt);
            paymentInfo.setGoodsTitle(PayHelper.GIFT_GOOGS_TITLES);
            paymentInfo.setGiftOrderId(order.getId());
            paymentInfo.setProjectId(order.getProjectId());
            clientIp = order.getIp();
        }

        String timeExpire = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(expireTime);
        paymentInfo.setTimeExpire(timeExpire);
        //创建支付信息记录
        paymentInfoRepo.save(paymentInfo);

        resultBuilder.payPlatform(UniformPayConstants.CUSTOM_PLATFORM);
        resultBuilder.orderNo(orderNo);

        if(isAlipay){
            AlipayResponse response =  alipayService.payOrderAlipay(paymentInfo);
            String payForm = response.getBody();
            resultBuilder.payForm(payForm);
        }else{
            Object result = weixinService.payOrderWeixin(paymentInfo, clientIp, request.getOpenId());
            if(result instanceof String){
                resultBuilder.payUrl(Objects.toString(result));
            }else{
                JSONObject payParams = (JSONObject)JSONObject.toJSON(result);
                resultBuilder.payParams(payParams);
            }
        }

        return resultBuilder.build();
    }

    @Override
    public UniformNotifyResult notify(UniformNotifyRequest request) throws Exception {
        UniformNotifyResult.UniformNotifyResultBuilder resultBuilder = UniformNotifyResult.builder();
        resultBuilder.content("");
        String orderNo = request.getOrderNo();
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付信息不存在"));

        //判断支付方式
        if(paymentInfo.getPayMethod() == PayMethod.ALIPAY){
            Map<String,String> params = request.getRequestParams();
            String gmt_payment = params.get("gmt_payment");//支付时间
            AlipaySignature.rsaCheckV1(params, alipayProperties.getAliPublicKey(), "UTF-8", "RSA2");
            String tradeStatus = MapUtils.getString(params, "trade_status");
            //TRADE_SUCCESS 交易成功 , TRADE_CLOSED 标识交易关闭
            if ("TRADE_SUCCESS".equals(tradeStatus)) {
                JSONObject body = JSON.parseObject(params.get("body"));
//                Long orderId = body.getLong("orderId");
//                Long giftOrderId = body.getLong("giftOrderId");
                String outTradeNo = Objects.toString(params.get("out_trade_no"),"");
                String transactionId = MapUtils.getString(params, "trade_no");

                //支付状态变更
                long eventTime = System.currentTimeMillis();
                paymentInfoService.notifyPaymentInfo(outTradeNo, transactionId, eventTime, gmt_payment);

                if(paymentInfo.getOrderId() != null){
                    SendResult result = rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
                            new OrderNotifyEvent(paymentInfo.getOrderId(), outTradeNo, PayMethod.ALIPAY, transactionId, eventTime ));
                    log.info("发送通知到队列: {}, result={}", paymentInfo.getOrderId(), result);
                }else{
                    context.publishEvent(new GiftOrderNotifyEvent(this, paymentInfo.getGiftOrderId(),
                            outTradeNo,
                            paymentInfo.getPayMethod(),transactionId));

                }

                resultBuilder.content("success");

            }

        }else if(paymentInfo.getPayMethod() == PayMethod.WEIXIN){
            String reqBody = request.getRequestBody();
            final WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(reqBody);
            notifyResult.checkResult(wxPayService, "MD5", true);
            String outTradeNo = notifyResult.getOutTradeNo();
            JSONObject attach = JSONObject.parseObject(notifyResult.getAttach());
//            Long orderId = attach.getLong("orderId");
//            Long giftOrderId = attach.getLong("giftOrderId");
            String transactionId = notifyResult.getTransactionId();
            //支付状态变更
            long eventTime = System.currentTimeMillis();
            paymentInfoService.notifyPaymentInfo(outTradeNo, transactionId, eventTime);

            if(paymentInfo.getOrderId() != null){
                SendResult result = rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
                        new OrderNotifyEvent(paymentInfo.getOrderId(), outTradeNo, PayMethod.ALIPAY, transactionId, eventTime ));
                log.info("发送通知到队列: {}, result={}", paymentInfo.getOrderId(), result);
            }else{
                context.publishEvent(new GiftOrderNotifyEvent(this, paymentInfo.getGiftOrderId(),
                        outTradeNo,
                        paymentInfo.getPayMethod(),transactionId));
            }

            String content = WxPayNotifyResponse.success("OK");
            resultBuilder.content(content);
        }else{
            throw new BusinessException("不支持的支付方式：" + paymentInfo.getPayMethod());
        }

        return resultBuilder.build();

    }

    @Override
    public Map<String, Object> refund(String orderNo, String refundReason) throws Exception {
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo)
                .orElseThrow(new BusinessException("支付信息记录不存在"));
        String transactionId = paymentInfo.getTransactionId();
        if(StringUtils.isEmpty(transactionId)){
            log.error("订单交易ID不存在，订单编号:" + orderNo);
            throw new BusinessException("订单交易ID不存在");
        }

        paymentInfo.setApplyRefundTime(LocalDateTime.now());
        paymentInfoRepo.save(paymentInfo);

        Map<String,Object> refundResult = null;
        String refundOrderNo =  String.valueOf(snowflakeIdWorker.nextId());
        if(paymentInfo.getPayMethod() == PayMethod.WEIXIN){
            WxPayRefundResult response = weixinService.refund(orderNo, refundOrderNo,refundReason);
            refundResult = (JSONObject) JSONObject.toJSON(response);
        }else if(paymentInfo.getPayMethod() == PayMethod.ALIPAY){
            AlipayResponse response = alipayService.refund(orderNo, refundOrderNo, refundReason);
            refundResult = (JSONObject)JSONObject.toJSON(response);
        }else{
            throw new BusinessException("暂不支持此支付方式的退款-" + paymentInfo.getPayMethod());
        }

        log.info("退款结果：{}, 订单编号:{}, 交易id：{}", refundResult, orderNo, transactionId);

        return refundResult;

    }

    @Override
    public UniformNotifyResult refundNotify(UniformNotifyRequest request) throws Exception {
        UniformNotifyResult.UniformNotifyResultBuilder resultBuilder = UniformNotifyResult.builder();
        String orderNo = request.getOrderNo();
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo)
                .orElseThrow(new BusinessException("支付信息记录不存在"));
        String reqBody = request.getRequestBody();
        String content = "";
        if(paymentInfo.getPayMethod() == PayMethod.WEIXIN){
            content = weixinService.refundNotify(orderNo, reqBody);
        }else if(paymentInfo.getPayMethod() == PayMethod.ALIPAY){
            content = alipayService.refundNotify(orderNo, reqBody);
        }else{
            throw new BusinessException("暂不支持此支付方式的退款通知-" + paymentInfo.getPayMethod());
        }

        //设置响应内容
        resultBuilder.content(content);
        return resultBuilder.build();

    }

    @Override
    public UniformNotifyResult transferNotify(UniformNotifyRequest request) throws Exception {
        throw new BusinessException("暂不支持");
    }

    @Override
    public UserBankCard addBankCard(UniformBindBank bindBank) throws Exception {
        Long userId = bindBank.getUserId();
        String bankNo = bindBank.getBankNo();
        String phone = bindBank.getPhone();
        String smsCode = bindBank.getSmsCode();
        //短信验证
        smsService.verify(phone, smsCode);

        User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
        IdentityAuth identityAuth = identityAuthRepo.findFirstByUserIdAndStatusAndDelFalseOrderByCreatedAtDesc(userId, AuthStatus.SUCCESS)
                .orElseThrow(new BusinessException("用户未通过实名认证"));
        if (identityAuth.isOrg()) {
            //throw new BusinessException("企业认证用户请绑定对公账户");
        }

        if(isBindBankCard(userId)){
            throw new BusinessException("此账号已绑定过，请先解绑");
        }

        BankValidate bankValidate = BankUtils.validate(bankNo);
        if (!bankValidate.isValidated()) {
            throw new BusinessException("暂不支持此卡");
        }

        UserBankCard userBankCard  = userBankCardRepo.save(UserBankCard.builder()
                .payPlatform(UniformPayConstants.CUSTOM_PLATFORM)
                .bank(bankValidate.getBank())
                .bankName(bankValidate.getBankName())
                .bankNo(bankNo)
                .cardType(bankValidate.getCardType())
                .cardTypeDesc(bankValidate.getCardTypeDesc())
                .userId(userId)
                .phone(phone)
                .realName(identityAuth.getRealName())
                .idNo(identityAuth.getIdNo())
                .build());

        return userBankCard;
    }

    @Override
    public void removeBankCard(Long userId) throws Exception {
        //直接解绑
        userBankCardRepo.deleteByUserId(userId);
    }

    @Override
    public boolean isBindBankCard(Long userId) {
        User owner = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
        UserBankCard userBankCard = userBankCardRepo.findByUserId(owner.getId()).stream().findAny()
                .orElse(null);
        if(userBankCard != null){
            return true;
        }
        return false;
    }
}
