package com.izouma.nineth.service.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.izouma.nineth.config.GeneralProperties;
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.DivMoneyService;
import com.izouma.nineth.service.IkudotpayService;
import com.izouma.nineth.service.PaymentInfoService;
import com.izouma.nineth.service.sms.SmsService;
import com.izouma.nineth.utils.BankUtils;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import com.izouma.nineth.utils.ikudot.IkudotpayClient;
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.HashMap;
import java.util.Map;

@Service(UniformPayConstants.IKUDOTPAY_PLATFORM)
@AllArgsConstructor
@Slf4j
public class IkudotpayPlatformImpl implements  UniformPayService{

    private static Map<String,PayMethod>  paymentMapToMethod = new HashMap<>();
    static{
        //接口支付映射到本地支付方式
        paymentMapToMethod.put("WXPAY", PayMethod.WEIXIN);
        paymentMapToMethod.put("ALIPAY", PayMethod.ALIPAY);
        paymentMapToMethod.put("UNIONPAY", PayMethod.BANK);
        paymentMapToMethod.put("BALANCE", null);
        paymentMapToMethod.put("CASH", null);
        paymentMapToMethod.put("ABC", null);
        paymentMapToMethod.put("SCRCU", null);
    }

    private static Map<String, String>  statusMap = new HashMap<>();
    static{
        statusMap.put("100","支付成功");
        statusMap.put("101","待支付");
        statusMap.put("102","用户支付中或等待输入密码");
        statusMap.put("104","支付失败");
        statusMap.put("105","订单已撤销或已关闭");
        statusMap.put("200","退款成功");
        statusMap.put("201","退款中");
        statusMap.put("202","退款失败");
        statusMap.put("203","退款超时或关闭");

    }


    private IkudotpayClient ikudotpayClient;

    private IkudotpayService ikudotpayService;


    private OrderRepo orderRepo;

    private GiftOrderRepo giftOrderRepo;

    private SnowflakeIdWorker snowflakeIdWorker;

    private PaymentInfoRepo paymentInfoRepo;

    private UserRepo userRepo;

    private GeneralProperties generalProperties;


    private IdentityAuthRepo identityAuthRepo;

    private UserBankCardRepo userBankCardRepo;

    private SmsService smsService;

    private CollectionRepo collectionRepo;

    private DivMoneyService divMoneyService;

    private PaymentInfoService paymentInfoService;

    private RocketMQTemplate rocketMQTemplate;

    private PayHelper payHelper;

    private ApplicationContext context;

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

        //不区分类型
        String payChannel = "kh5_pay";

        LocalDateTime expireTime = null;
        String orderNo = String.valueOf(snowflakeIdWorker.nextId());
        String notifyUrl = payHelper.resolvePayNotifyUrl(orderNo);
        //本地保存支付信息
        PaymentInfo paymentInfo = PaymentInfo.builder()
                 .orderNo(orderNo)
                .payMethod(null).payChannel(payChannel)
                .status(PayStatus.UNPAID)
                .merchantName(null)
                .notifyUrl(notifyUrl).payPlatform(UniformPayConstants.IKUDOTPAY_PLATFORM).build();
        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());
            //开始记录分账
            divMoneyService.saveDivMoney(order, collection, UniformPayConstants.IKUDOTPAY_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);

        JSONObject result = ikudotpayService.h5pay(paymentInfo);

        UniformPayResult.UniformPayResultBuilder resultBuilder = UniformPayResult.builder();
        resultBuilder.payPlatform(UniformPayConstants.IKUDOTPAY_PLATFORM);
        resultBuilder.orderNo(orderNo);
        resultBuilder.payParams(result);
        String url = result.getString("url");
        resultBuilder.payUrl(url);

        return resultBuilder.build();
    }

    @Override
    public UniformNotifyResult notify(UniformNotifyRequest request){
        UniformNotifyResult.UniformNotifyResultBuilder resultBuilder = UniformNotifyResult.builder();
        String orderNo = request.getOrderNo();
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo)
                .orElseThrow(new BusinessException("支付信息记录不存在"));
        String reqData = request.getRequestBody();
        JSONObject commonWrapper = JSON.parseObject(reqData);

        JSONObject reqJson = ikudotpayClient.parseRequest(commonWrapper);

        String payTime = reqJson.getString("pay_time");//支付时间，格式"yyyy-MM-dd HH:mm:ss"
        String payFinishTime = reqJson.getString("pay_finish_time");//只有在支付成功时返回，格式"yyyy-MM-dd HH:mm:ss"
        String mchId = reqJson.getString("mch_id");
        String payNo = reqJson.getString("pay_no");//聚合支付订单号
        //String orderNo = reqJson.getString("out_trade_no");
        String transactionId = reqJson.getString("transaction_id");//渠道交易流水号
        String channelTransactionId = reqJson.getString("channel_transaction_id");//间接渠道交易流水
        String payment = reqJson.getString("payment");//支付方式
        String status = reqJson.getString("status");//支付状态
        String attach = reqJson.getString("attach");//附加信息

        long eventTime  = System.currentTimeMillis();

        if(!"100".equals(status)){
            log.warn("酷点收到支付通知，orderNo:" + orderNo + ",状态：" + status + ",状态说明：" + statusMap.get(status));
            paymentInfoService.failedPaymentInfo(orderNo, transactionId, eventTime, payTime);
            resultBuilder.content("SUCCESS");
            return resultBuilder.build();
        }

        PayMethod payMethod = paymentMapToMethod.get(payment);

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

        if(paymentInfo.getOrderId() != null){
            SendResult result = rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
                    new OrderNotifyEvent(paymentInfo.getOrderId(), orderNo, payMethod, transactionId, eventTime ));

            log.info("发送通知到队列: {}, result={}", paymentInfo.getOrderId(), result);
        }else{
            context.publishEvent(new GiftOrderNotifyEvent(this, paymentInfo.getGiftOrderId(),
                    orderNo,
                    payMethod,transactionId));

        }

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

    @Override
    public Map<String, Object> refund(String orderNo, String refundReason){

        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo)
                .orElseThrow(new BusinessException("支付信息记录不存在"));
        paymentInfo.setApplyRefundTime(LocalDateTime.now());
        paymentInfoRepo.save(paymentInfo);

        String refundNo = String.valueOf(snowflakeIdWorker.nextId());
        String notifyUrl = payHelper.resolveRefundNotifyUrl(orderNo);
        int fee =  paymentInfo.getPayAmt().multiply(new BigDecimal(100)).intValue();
        JSONObject reqData = new JSONObject();
        reqData.put("pay_no", paymentInfo.getThirdPayNo());//聚合支付订单号
        reqData.put("out_trade_no", paymentInfo.getOrderNo() );//原订单号

        if(StringUtils.isNotEmpty(paymentInfo.getTransactionId())){
            //交易流水
            reqData.put("transaction_id", paymentInfo.getTransactionId() );
        }

        reqData.put("out_refund_no", refundNo );
        reqData.put("fee", fee);
        reqData.put("refund_fee", fee);
        reqData.put("remark", refundReason);
        reqData.put("notify_url", notifyUrl);

        JSONObject result = ikudotpayClient.postJson("/openapi/pay/refund", reqData);

        return result;
    }

    @Override
    public UniformNotifyResult refundNotify(UniformNotifyRequest request){
        UniformNotifyResult.UniformNotifyResultBuilder resultBuilder = UniformNotifyResult.builder();
        String orderNo = request.getOrderNo();
        String reqData = request.getRequestBody();
        JSONObject commonWrapper = JSON.parseObject(reqData);

        JSONObject reqJson = ikudotpayClient.parseRequest(commonWrapper);
        String refundNo = reqJson.getString("refund_no");//聚合支付退款订单号
        String outRefundNo = reqJson.getString("out_refund_no");//业务系统退款订单号，业务方保证唯一
        String thirdRefundId = reqJson.getString("third_refund_id");
        String payNo = reqJson.getString("pay_no");
        String status = reqJson.getString("status");//支付状态


        //退款成功判断，200
        if(!"200".equals(status)){
            log.warn("酷点收到退款通知，orderNo:" + orderNo + ",状态：" + status + ",状态说明：" + statusMap.get(status));
            resultBuilder.content("SUCCESS");
            return resultBuilder.build();
        }

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

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

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

    @Override
    @Transactional
    public UserBankCard addBankCard(UniformBindBank bindBank){
        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.IKUDOTPAY_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){
        //直接解绑
        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;
    }


}
