package com.izouma.nineth.service.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.huifu.adapay.core.AdapayCore;
import com.huifu.adapay.core.util.AdapaySign;
import com.huifu.adapay.model.AdapayCommon;
import com.huifu.adapay.model.Payment;
import com.huifu.adapay.model.Refund;
import com.izouma.nineth.config.AdapayProperties;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.domain.Collection;
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 javax.transaction.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 汇付支付实现
 *
 */
@Service(UniformPayConstants.ADAPAY_PLATFORM)
@AllArgsConstructor
@Slf4j
public class AdapayPlatformImpl implements  UniformPayService{

    private UserRepo userRepo;
    private OrderRepo orderRepo;
    private GiftOrderRepo giftOrderRepo;

    private CollectionRepo collectionRepo;

    private PaymentInfoService paymentInfoService;

    private PaymentInfoRepo paymentInfoRepo;

    private SnowflakeIdWorker snowflakeIdWorker;

    private AdapayProperties adapayProperties;

    private GeneralProperties generalProperties;

    private final RocketMQTemplate rocketMQTemplate;

    private AdapayMerchantService adapayMerchantService;

    private IdentityAuthRepo      identityAuthRepo;

    private UserBankCardRepo      userBankCardRepo;

    private SmsService smsService;

    private DivMoneyService divMoneyService;

    private PayHelper payHelper;

    private ApplicationContext context;

    @Cacheable(value = UniformPayConstants.ADAPAY_PLATFORM, key = "#request.uniqueId")
    @Override
    public UniformPayResult pay(UniformPayRequest request)  throws Exception{
        //通过交易类型，判断是支付宝还是微信
        TradeType tradeType = request.getTradeType();
        String openId = request.getOpenId();

        String payChannel = null;
        switch(tradeType){
            case ALIPAY_WAP:
                payChannel = "alipay_wap";
                break;
            case ALIPAY_APP:
                payChannel = "alipay";
                break;
            case ALIPAY_QR:
                payChannel = "alipay_qr";
                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(adapayProperties.getMerchant())
                .notifyUrl(notifyUrl).payPlatform(UniformPayConstants.ADAPAY_PLATFORM).build();
        List<DivMoney> divMoneyList = 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("藏品不存在"));
            String goodsTitle = collection.getName();
            BigDecimal payAmt = order.getTotalPrice().setScale(2, RoundingMode.HALF_UP);
            paymentInfo.setUserId(order.getUserId());
            paymentInfo.setPayAmt(payAmt);
            paymentInfo.setGoodsTitle(goodsTitle);
            paymentInfo.setOrderId(order.getId());
            paymentInfo.setProjectId(order.getProjectId());
            //开始记录分账
            divMoneyList = divMoneyService.saveDivMoney(order, collection, UniformPayConstants.ADAPAY_PLATFORM);
        }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());
        }

        String timeExpire = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(expireTime);
        paymentInfo.setTimeExpire(timeExpire);
        paymentInfoRepo.save(paymentInfo);

        String returnUrl = payHelper.resolveReturnUrl(paymentInfo);

        //支付方创建支付信息
        Map<String, Object> paymentParams = new HashMap<>();
        paymentParams.put("order_no", orderNo);
        paymentParams.put("pay_amt", paymentInfo.getPayAmt().toPlainString());
        paymentParams.put("app_id", adapayProperties.getAppId());
        paymentParams.put("pay_channel", payChannel);
        paymentParams.put("goods_title", paymentInfo.getGoodsTitle());
        paymentParams.put("goods_desc", paymentInfo.getGoodsTitle());
        paymentParams.put("time_expire", timeExpire);
        paymentParams.put("notify_url", notifyUrl);

        List<Map<String, Object>> divMembers = new ArrayList<>();
        if(divMoneyList != null) {
            divMoneyList.forEach(divMoney -> {
                Map<String, Object> divMem = new HashMap<>();
                divMem.put("member_id", divMoney.getMemberId());
                divMem.put("amount", divMoney.getAmount().toPlainString());
                divMem.put("fee_flag", divMoney.isFeeFlag() ? "Y" : "N");
                divMembers.add(divMem);
            });
        }

        if (divMembers.size() > 1) {
            paymentParams.put("div_members", divMembers);
        }
        Map<String, Object> expend = new HashMap<>();
        paymentParams.put("expend", expend);
        if ("wx_pub".equals(payChannel)) {
            if (StringUtils.isBlank(openId)) {
                throw new BusinessException("缺少openId");
            }
            expend.put("open_id", openId);
            expend.put("limit_pay", "1");
        }

        Map<String, Object> response;
        if ("wx_lite".equals(payChannel)) {
            paymentParams.put("adapay_func_code", "wxpay.createOrder");
            paymentParams.put("callback_url", returnUrl);
            response = AdapayCommon.requestAdapayUits(paymentParams);
            log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
        } else {
            response = Payment.create(paymentParams);
            log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
            AdapayMerchantService.checkSuccess(response);

            String id = MapUtils.getString(response, "id");
            paymentInfo.setTransactionId(id);
        }

        UniformPayResult.UniformPayResultBuilder resultBuilder = UniformPayResult.builder();
        resultBuilder.payPlatform(UniformPayConstants.ADAPAY_PLATFORM);
        resultBuilder.orderNo(orderNo);
        switch (payChannel) {
            case "alipay_wap":
            case "alipay":
                String payUrl =  MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info");
                resultBuilder.payForm(payUrl);
                break;
            case "alipay_qr":
                String qrcode = MapUtils.getString(MapUtils.getMap(response, "expend"), "qrcode_url");
                resultBuilder.payUrl(qrcode);
                break;
            case "wx_pub":
                JSONObject payParams = JSON.parseObject(MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info"));
                payParams.put("timestamp", payParams.get("timeStamp"));
                payParams.remove("timeStamp");
                resultBuilder.payParams(payParams);
                break;
            default:
                Map<String,Object> extend = MapUtils.getMap(response, "expend");
                resultBuilder.payParams(extend);
        }
        //如果存在交易流水，可以补充
        return resultBuilder.build();
    }

    @Override
    public UniformNotifyResult notify(UniformNotifyRequest request)  throws Exception {
        UniformNotifyResult.UniformNotifyResultBuilder resultBuilder = UniformNotifyResult.builder();
        //设置默认响应内容
        resultBuilder.content("error");
        String orderNo = request.getOrderNo();
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo)
                .orElseThrow(new BusinessException("支付信息记录不存在"));
        String data = request.getParameter("data");
        String sign = request.getParameter("sign");
        String type = request.getParameter("type");
        if ("payment.succeeded".equals(type)) {
            boolean checkSign = AdapaySign.verifySign(data, sign, AdapayCore.PUBLIC_KEY);
            log.info("checkSign {}", checkSign);
            if (checkSign) {
                JSONObject jsonObject = JSON.parseObject(data);
                String channel = jsonObject.getString("pay_channel");
                //String orderNo = jsonObject.getString("order_no");
                String transactionId = jsonObject.getString("id");
                PayMethod payMethod = channel.startsWith("wx") ? PayMethod.WEIXIN : PayMethod.ALIPAY;

                //先修改支付状态，防止查不到的问题
                long eventTime  = System.currentTimeMillis();
                paymentInfoService.notifyPaymentInfo(orderNo, transactionId, eventTime);

                if(paymentInfo.getOrderId() != null){
                    SendResult result = rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
                            new OrderNotifyEvent(paymentInfo.getOrderId(), orderNo, payMethod, transactionId, eventTime ));
                    log.info("发送通知到队列: {}, result={}", paymentInfo.getOrderId(), result);
                    //完成分账状态
                    divMoneyService.completeDivMoney(paymentInfo.getOrderId());
                }else{
                    context.publishEvent(new GiftOrderNotifyEvent(this, paymentInfo.getGiftOrderId(),
                            orderNo,
                            paymentInfo.getPayMethod(),transactionId));

                }


                //设置响应内容
                resultBuilder.content("success");

            }
        }

        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不存在，订单编号:" + paymentInfo.getOrderNo());
            throw new BusinessException("订单交易ID不存在");
        }

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

        String refundOrderNo =  String.valueOf(snowflakeIdWorker.nextId());
        Map<String, Object> refundParams = new HashMap<>();
        String refundAmt  = paymentInfo.getPayAmt().toPlainString();
        refundParams.put("refund_amt", refundAmt);
        refundParams.put("refund_order_no", refundOrderNo);
        Map<String, Object> response = Refund.create(transactionId, refundParams);
        log.info("退款结果：{}, 订单编号:{}, 交易id：{}", response, orderNo, transactionId);

        //更改支付信息状态
        paymentInfoService.completeRefund(orderNo, refundOrderNo);

        return response;
    }

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

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

    @Override
    @Transactional
    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.ADAPAY_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());

        //支付方绑定
        adapayMerchantService.createMemberForAll(userId.toString(), user.getPhone(),
                userBankCard.getRealName(), userBankCard.getIdNo());

        String accountId = adapayMerchantService.createSettleAccountForAll
                (user.getId().toString(), identityAuth.getRealName(),
                        identityAuth.getIdNo(), phone, bankNo);
        user.setSettleAccountId(Optional.ofNullable(accountId).orElse("1"));
//        user.setBankCard(true);
        userRepo.save(user);

        userBankCard.setThirdAccountId(accountId);
        userBankCardRepo.save(userBankCard);

        return userBankCard;
    }

    @Override
    public void removeBankCard(Long userId) throws Exception {
        User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
        //移除支付通道账户
        if (StringUtils.isNotBlank(user.getSettleAccountId())) {
            adapayMerchantService.delSettleAccountForAll(user.getId().toString());
            user.setSettleAccountId(null);
            userRepo.save(user);

        }

        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 && StringUtils.isNotBlank(owner.getSettleAccountId()) ){
            return true;
        }

        return false;
    }


}
