package com.izouma.nineth.service.pay;

import com.alibaba.fastjson.JSONObject;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.config.RedisKeys;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.BankValidate;
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.lock.RedisLockable;
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.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.List;
import java.util.Map;

/**
 * 连连付
 *
 */
@Service(UniformPayConstants.LIANLIANPAY_PLATFORM)
@AllArgsConstructor
@Slf4j
public class LianlianpayPlatformImpl implements UniformPayService{

    private GeneralProperties generalProperties;

    private LianlianPayService lianlianPayService;

    private OrderRepo orderRepo;

    private GiftOrderRepo giftOrderRepo;

    private CollectionRepo collectionRepo;

    private SnowflakeIdWorker snowflakeIdWorker;

    private PaymentInfoRepo paymentInfoRepo;

    private DivMoneyRepo divMoneyRepo;

    private DivMoneyService divMoneyService;

    private PaymentInfoService paymentInfoService;

    private RocketMQTemplate rocketMQTemplate;

    private UserRepo userRepo;

    private UserBankCardRepo userBankCardRepo;

    private SmsService smsService;

    private IdentityAuthRepo identityAuthRepo;

    private PayHelper payHelper;

    private ApplicationContext context;

    private UserFundAccountRepo userFundAccountRepo;

    private UserFundAccountForLianlianpayService userFundAccountForLianlianpayService;

    private UserFundChangeLogService userFundChangeLogService;

    private TransferMoneyInfoRepo transferMoneyInfoRepo;

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

        PayMethod payMethod;
        String payChannel;
        switch (tradeType){
            case WALLET_ACCOUNT:
                payChannel = "wallet";
                payMethod = PayMethod.WALLET;
                break;
            case QUICK_PAY:
                payChannel = "quickpay";
                payMethod = PayMethod.BANK;
                break;
            default:
                throw new BusinessException("不支持的交易方式-" + tradeType);

        }

        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.LIANLIANPAY_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("藏品不存在"));

            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());
            //开始记录分账
            divMoneyList = divMoneyService.saveDivMoney(order, collection, UniformPayConstants.LIANLIANPAY_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);


        //分账检查
        if(divMoneyList != null){
            divMoneyList.forEach((item) -> {
                if(item.getUserId() != null){
                    UserFundAccount seller = userFundAccountForLianlianpayService.getAccount(item.getUserId());
                    if(seller == null || seller.getAccountStatus() != AccountStatus.NORMAL){
                        throw new BusinessException("卖家未开通钱包，无法支付");
                    }
                    //记录卖家账户ID
                    item.setUserFundAccountId(seller.getId());
                    divMoneyRepo.save(item);
                }

            });
        }

        //支持匿名用户，不注册钱包也可以支付
        boolean hasWallet = false;
        UserFundAccount userFundAccount = userFundAccountForLianlianpayService.getAccount(paymentInfo.getUserId());
        if(userFundAccount != null && userFundAccount.getAccountStatus() == AccountStatus.NORMAL){
            hasWallet = true;
            paymentInfo.setUserFundAccountId(userFundAccount.getId());
            paymentInfoRepo.save(paymentInfo);
        }

        JSONObject res = lianlianPayService.payCreate(paymentInfo, divMoneyList, hasWallet);
        resultBuilder.payParams(res);
        resultBuilder.orderNo(orderNo);

        return resultBuilder.build();
    }

    /**
     * 并发控制重试，防止修改状态覆盖
     * @param request
     * @return
     * @throws Exception
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#request.orderNo",
            isWaiting = true, retryCount=20, retryWaitingTime = 100)
    @Override
    public UniformNotifyResult notify(UniformNotifyRequest request) throws Exception {
        UniformNotifyResult.UniformNotifyResultBuilder resultBuilder = UniformNotifyResult.builder();
        resultBuilder.content("Success");
        String orderNo = request.getOrderNo();
        String reqBody = request.getRequestBody();

        String fromSign = request.getHeader("Signature-Data");

        JSONObject bodyJson = lianlianPayService.checkSuccessBody(reqBody, fromSign);

        String payTime = bodyJson.getString("finish_time");//格式：yyyyMMddHHmmss
        String transactionId = bodyJson.getString("accp_txno");//系统交易单号
        String orderStatus = bodyJson.getString("txn_status");
        String linkedAgrtno = bodyJson.getString("linked_agrtno");//首次支付成功返回，绑卡协议号

        long eventTime  = System.currentTimeMillis();

        //1：成功
        if(!"TRADE_SUCCESS".equals(orderStatus)){
            log.warn("连连付收到支付通知，orderNo:" + orderNo + ",状态：" + orderStatus);
            paymentInfoService.failedPaymentInfo(orderNo, transactionId, eventTime, payTime);
            return resultBuilder.build();
        }


        //先修改支付状态，防止查不到的问题
        PaymentInfo paymentInfo = paymentInfoService.notifyPaymentInfo(orderNo, transactionId, eventTime, payTime);
        PayMethod payMethod = null;
        if(paymentInfo != null){
            payMethod = paymentInfo.getPayMethod();
        }

        //更新快捷支付绑卡协议
        if(StringUtils.isNotEmpty(linkedAgrtno) && paymentInfo.getUserBankCardId() != null){
            UserBankCard userBankCard = userBankCardRepo.findById(paymentInfo.getUserBankCardId()).orElse(null);
            if(userBankCard != null){
                userBankCard.setThirdBankId(linkedAgrtno);
                userBankCardRepo.save(userBankCard);
            }
        }

        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));

        }

        //设置响应内容
        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);
            throw new BusinessException("订单交易ID不存在");
        }

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

        //记录退款流水
        userFundChangeLogService.createByRefund(paymentInfo);

        //统一退款
        JSONObject resultJson = lianlianPayService.accountPayRefund(paymentInfo, refundReason);

        return resultJson;
    }

    @Override
    public UniformNotifyResult refundNotify(UniformNotifyRequest request) throws Exception {
        UniformNotifyResult.UniformNotifyResultBuilder resultBuilder = UniformNotifyResult.builder();
        String orderNo = request.getOrderNo();
        String reqBody = request.getRequestBody();
        String fromSign = request.getHeader("Signature-Data");
        JSONObject bodyJson = lianlianPayService.checkSuccessBody(reqBody, fromSign);

        String accpTxno = bodyJson.getString("accp_txno");//系统交易单号
        String refundNo = bodyJson.getString("refund_seqno");//退款单号
        String chnlTxno = bodyJson.getString("chnl_txno");//退款流水
        String orderStatus = bodyJson.getString("txn_status");

        if("TRADE_SUCCESS".equals(orderStatus)){
            //更改支付信息状态
            paymentInfoService.completeRefund(orderNo, refundNo);
        }else{
            log.warn("连连付收到退款通知，orderNo:" + orderNo + ",状态：" + orderStatus);
        }


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

    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#request.orderNo",
            isWaiting = true, retryCount=20, retryWaitingTime = 100)
    @Override
    public UniformNotifyResult transferNotify(UniformNotifyRequest request) throws Exception {
        UniformNotifyResult.UniformNotifyResultBuilder resultBuilder = UniformNotifyResult.builder();
        String orderNo = request.getOrderNo();
        String reqBody = request.getRequestBody();
        String fromSign = request.getHeader("Signature-Data");
        JSONObject bodyJson = lianlianPayService.checkSuccessBody(reqBody, fromSign);

        String accpTxno = bodyJson.getString("accp_txno");//系统交易单号
        String chnlTxno = bodyJson.getString("chnl_txno");//渠道交易单号
        String orderStatus = bodyJson.getString("txn_status");

        TransferMoneyInfo transferMoneyInfo = transferMoneyInfoRepo.findByTradeNo(orderNo)
                .orElseThrow(new BusinessException("未找到转账记录"));

        if("TRADE_SUCCESS".equals(orderStatus)){
            //更改支付信息状态
            transferMoneyInfo.setFinishTime(LocalDateTime.now());
            transferMoneyInfo.setStatus(TransferStatus.SUCCESS);
            transferMoneyInfo.setResult("交易成功");
            transferMoneyInfoRepo.save(transferMoneyInfo);

            userFundChangeLogService.updateSuccess(transferMoneyInfo.getToUserId(),
                    FundChangeType.INCOME,transferMoneyInfo.getTradeNo());
        }else{
            log.warn("连连付收到转账通知，orderNo:" + orderNo + ",状态：" + orderStatus);
        }


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

    @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();
        String bindOrderNo = bindBank.getBindOrderNo();
        String token = bindBank.getToken();

        if(StringUtils.isEmpty(bindOrderNo) || StringUtils.isEmpty(token)){
            throw new BusinessException("绑卡订单号或授权token不能为空");
        }

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

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


        //短信验证
        JSONObject res = lianlianPayService.bindCardVerify(userId, bindOrderNo, token ,smsCode);

        String linkedAgrtno = res.getString("linked_agrtno");


        //修改协议号和真实卡号
        UserFundAccount account = userFundAccountForLianlianpayService.getNormalAccount(userId);

        UserBankCard userBankCard = userBankCardRepo.save(UserBankCard.builder()
                .payPlatform(UniformPayConstants.LIANLIANPAY_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())
                .bindOrderNo(bindOrderNo)
                .token(token)
                .build());
        userBankCard.setThirdAccountId(account.getThirdUserId());
        userBankCard.setThirdBankId(linkedAgrtno);
        userBankCard = userBankCardRepo.save(userBankCard);


        //变更绑定的卡号信息
        account.setUserBankCardId(userBankCard.getId());
        account.setThirdBankId(linkedAgrtno);
        userFundAccountRepo.save(account);

        return userBankCard;
    }

    @Override
    public void removeBankCard(Long userId) throws Exception {
        //修改协议号和真实卡号
        UserFundAccount account = userFundAccountForLianlianpayService.getAccount(userId);
        if(account != null && (account.getAccountStatus() == AccountStatus.NORMAL || account.getAccountStatus() == AccountStatus.CHECK_PENDING)){
            throw new BusinessException("开通钱包后暂不支持解绑银行卡");
        }

        //直接解绑
        userBankCardRepo.softDeleteByUserId(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;
    }


}
