package com.izouma.nineth.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayResponse;
import com.alipay.api.domain.AlipayFundTransToaccountTransferModel;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.izouma.nineth.config.AlipayProperties;
import com.izouma.nineth.config.Constants;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.enums.*;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.service.pay.PayHelper;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 支付宝支付服务
 */
@Service
@AllArgsConstructor
@Slf4j
public class AlipayService {

    private GeneralProperties generalProperties;

    private AlipayProperties alipayProperties;

    private AlipayClient alipayClient;
    private PayHelper payHelper;

    private OrderRepo orderRepo;

    private PaymentInfoRepo paymentInfoRepo;

    private PaymentInfoService paymentInfoService;

    private DivMoneyRepo divMoneyRepo;

    private TransferMoneyInfoRepo transferMoneyInfoRepo;

    private SnowflakeIdWorker snowflakeIdWorker;

    private UserFundAccountService userFunAccountService;

    /**
     * 支付
     * @param paymentInfo
     * @return
     * @throws Exception
     */
    public AlipayResponse payOrderAlipay(PaymentInfo paymentInfo) throws Exception{
        String totalAmount = paymentInfo.getPayAmt().stripTrailingZeros().toPlainString();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", paymentInfo.getOrderNo());
        bizContent.put("total_amount", totalAmount);
        bizContent.put("disable_pay_channels", "pcredit,creditCard");
        bizContent.put("subject", paymentInfo.getGoodsTitle());
        bizContent.put("product_code", "QUICK_WAP_PAY");
        JSONObject body = new JSONObject();
        body.put("userId", paymentInfo.getUserId());
        body.put("orderId", paymentInfo.getOrderId());
        body.put("giftOrderId", paymentInfo.getGiftOrderId());
        bizContent.put("body", body.toJSONString());

        AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
        String returnUrl = payHelper.resolveReturnUrl(paymentInfo);
        alipayRequest.setReturnUrl(returnUrl);
        //AlipayTradePrecreateRequest alipayRequest = new AlipayTradePrecreateRequest();
        //alipayRequest.setReturnUrl(alipayProperties.getReturnUrl());
        alipayRequest.setNotifyUrl(paymentInfo.getNotifyUrl());
        alipayRequest.setBizContent(JSON.toJSONString(bizContent));

        AlipayResponse response = alipayClient.pageExecute(alipayRequest);
        log.info("支付宝支付响应内容：" + JSONObject.toJSONString(response));
        if(!response.isSuccess()){
            throw new BusinessException(response.getSubMsg() + "("+response.getSubCode()+")");
        }
        return response;
    }


    /**
     * 退款执行
     * @param orderNo
     * @return
     * @throws AlipayApiException
     */
    public AlipayResponse refund(String orderNo, String refundOrderNo, String refundReason) throws AlipayApiException {

        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付信息不存在"));
        //不用给通知地址，支付宝会回调以前支付的回调地址，通知状态：TRADE_CLOSED
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", paymentInfo.getOrderNo());
        bizContent.put("trade_no", paymentInfo.getTransactionId());
        bizContent.put("out_request_no", refundOrderNo);
        String refundAmount = paymentInfo.getPayAmt().stripTrailingZeros().toPlainString();
        bizContent.put("refund_amount", refundAmount);
        bizContent.put("refund_reason", refundReason);

        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        //request.setNotifyUrl(notifyUrl);
        request.setBizContent(JSONObject.toJSONString(bizContent));
        AlipayResponse response = alipayClient.execute(request);

        if(!response.isSuccess()){
            throw new BusinessException(response.getSubMsg() + "("+response.getSubCode()+")");
        }

        //实时响应，直接标记为退款
        paymentInfoService.completeRefund(paymentInfo.getOrderNo(), refundOrderNo);

        return response;

    }


    /**
     * 退款通知响应
     * @param reqBody
     * @return
     */
    public String refundNotify(String orderNo, String reqBody) throws Exception{
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付信息不存在"));
        Map<String,String> params = new LinkedHashMap<>();
        String originalStr = URLDecoder.decode(reqBody, StandardCharsets.UTF_8.name());
        String[] pairAry = originalStr.split("&");
        for(String pair : pairAry){
            String[] mp = pair.split("=");
            params.put(mp[0], mp.length > 1 ? mp[1] : "");
        }

        AlipaySignature.rsaCheckV1(params, alipayProperties.getAliPublicKey(), "UTF-8", "RSA2");

        String outRequestNo = params.get("out_request_no");//退款请求号
        String outTradeNo = params.get("out_trade_no");//订单号
        String tradeNo = params.get("trade_no");//支付宝交易号

        //更改支付信息状态
        paymentInfoService.completeRefund(paymentInfo.getOrderNo(), outRequestNo);

        return "success";
    }

    /**
     * 单笔转账交易
     *
     * @return
     */
    public AlipayResponse transfer(String outBizNo, String orderTitle,
                                   String remark,
                                   BigDecimal transferAmount, UserFundAccount targetAccount) throws AlipayApiException{

        String totalAmount = transferAmount.stripTrailingZeros().toPlainString();
        AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
        AlipayFundTransUniTransferModel bizModel = new AlipayFundTransUniTransferModel();
        bizModel.setOutBizNo(outBizNo);
        bizModel.setTransAmount(totalAmount);
        bizModel.setProductCode("TRANS_ACCOUNT_NO_PWD");
        bizModel.setBizScene("DIRECT_TRANSFER");
        bizModel.setOrderTitle(orderTitle);

        Participant participant = new Participant();
        participant.setIdentity(targetAccount.getAccountNo());
        if(targetAccount.getAccountType() == AccountType.ALIPAY_USER_ID){
            participant.setIdentityType("ALIPAY_USER_ID");
        }else if(targetAccount.getAccountType()  == AccountType.ALIPAY_LOGON_ID){
            participant.setIdentityType("ALIPAY_LOGON_ID");
        }else{
            throw new BusinessException("不支持的账户类型：" + targetAccount.getAccountType());
        }

        participant.setName(targetAccount.getAccountName());
        bizModel.setPayeeInfo(participant);
        bizModel.setRemark(remark);
        request.setBizModel(bizModel);

        AlipayResponse response = alipayClient.execute(request);
        log.info("支付宝转账响应内容：" + JSONObject.toJSONString(response));
        return response;
    }


    /**
     * 转账给分账用户
     * @param divMoneyId
     * @return
     */
    @Transactional
    public TransferMoneyInfo transferDivMoney(Long divMoneyId, String reason){
        DivMoney divMoney = divMoneyRepo.findById(divMoneyId).orElseThrow(new BusinessException("分账记录不存在"));
        if(divMoney.getUserId() == null){
            throw new BusinessException("分账的用户ID不存在");
        }

        Order order = orderRepo.findById(divMoney.getOrderId()).orElseThrow(new BusinessException("分账订单未找到"));
        if(order.getStatus() != OrderStatus.FINISH){
            throw new BusinessException("订单状态不正确");
        }

        if(divMoney.getAmount().compareTo(order.getPrice()) > 0){
            throw new BusinessException("分账金额不正确");
        }

        //查找支付宝账户
        UserFundAccount primaryAccount = userFunAccountService.getPrimaryAlipayAccount(divMoney.getUserId());
        if(primaryAccount == null){
            throw new BusinessException("用户未绑定支付宝资金账户，分账失败");
        }

        if(primaryAccount.getAccountStatus() != AccountStatus.NORMAL){
            throw new BusinessException("用户绑定的支付宝账户状态不正常");
        }

        TransferMethod transferMethod = null;
        switch(primaryAccount.getAccountType()){
            case ALIPAY_USER_ID:
            case ALIPAY_LOGON_ID:
                transferMethod = TransferMethod.ALIPAY;
                break;
            default:
                throw new BusinessException("不支持的资金账户类型：" + primaryAccount.getAccountType());
        }

        //无论成功与否，直接标记已处理
        divMoney.setStatus(DivStatus.PROCESSED);
        divMoney.setProcessTime(LocalDateTime.now());



        if(divMoney.getTransferMoneyId() != null){
            //历史记录
            TransferMoneyInfo transferMoneyInfo = transferMoneyInfoRepo.findById(divMoney.getTransferMoneyId()).orElseThrow(new BusinessException("历史转账记录不存在"));
            //是否账户不重复
            if(transferMoneyInfo.getAccountNo().equals(primaryAccount.getAccountNo())){
                throw new BusinessException("不允许对重复的账户重新转账");
            }
        }

        //保存转账记录
        String tradeNo = String.valueOf(snowflakeIdWorker.nextId());
        TransferMoneyInfo.TransferMoneyInfoBuilder builder = TransferMoneyInfo.builder();
        builder.tradeNo(tradeNo).toUserId(divMoney.getUserId())
                .transferMethod(transferMethod)
                .accountType(primaryAccount.getAccountType().name())
                .accountNo(primaryAccount.getAccountNo())
                .accountName(primaryAccount.getAccountName())
                .amount(divMoney.getAmount())
                .payPlatform(divMoney.getPayPlatform())
                .reason(reason)
                .divMoneyId(divMoneyId);
        TransferMoneyInfo transferMoneyInfo = builder.build();
        //保存
        transferMoneyInfoRepo.save(transferMoneyInfo);
        //记录转账记录ID
        divMoney.setTransferMoneyId(transferMoneyInfo.getId());


        try {
            String remark =  "分润订单ID" + order.getId();
            AlipayResponse response = transfer(transferMoneyInfo.getTradeNo(), reason, remark,
                    transferMoneyInfo.getAmount(), primaryAccount);

            if(!response.isSuccess()){
                throw new BusinessException(response.getSubMsg() + "("+response.getSubCode()+")");
            }

            JSONObject bodyData = JSONObject.parseObject(response.getBody());
            JSONObject transferRes = bodyData.getJSONObject("alipay_fund_trans_uni_transfer_response");
            String transferOrderId = transferRes.getString("order_id");
            String transactionId = transferRes.getString("pay_fund_order_id");
            String transferDate = transferRes.getString("transfer_date");
            //SUCCESS:转账成功
            //FAILED:转账失败
            //PROCESSING:处理中
            String status = transferRes.getString("status");

            transferMoneyInfo.setTransferOrderId(transferOrderId);
            transferMoneyInfo.setTransactionId(transactionId);
            transferMoneyInfo.setTransferDate(transferDate);
            TransferStatus transferStatus = null;
            if("SUCCESS".equals(status)){
                transferStatus = TransferStatus.SUCCESS;
            }else if("FAILED".equals(status)){
                transferStatus = TransferStatus.FAILED;
            }else if("PROCESSING".equals(status)){
                transferStatus = TransferStatus.PROCESSING;
            }else{
                log.error("转账状态未知,转账订单订单：{}， 状态：{}" , transferMoneyInfo.getTradeNo(),  status);
            }

            transferMoneyInfo.setStatus(transferStatus);
            transferMoneyInfoRepo.save(transferMoneyInfo);
            divMoney.setTransferResult(status);
        }catch(Exception e){
            log.error("分润转账失败，分账记录ID:" + divMoneyId, e);
            transferMoneyInfo.setStatus(TransferStatus.FAILED);
            transferMoneyInfo.setResult(e.getMessage());
            divMoney.setStatus(DivStatus.ERROR);
            divMoney.setTransferResult(e.getMessage());
        }

        transferMoneyInfoRepo.save(transferMoneyInfo);
        divMoneyRepo.save(divMoney);

        return transferMoneyInfo;
    }



}
