package com.leone.pay.ali.service;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundTransToaccountTransferModel;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.*;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leone.pay.common.Result;
import com.leone.pay.common.enums.status.OrderStatus;
import com.leone.pay.common.exception.ExceptionMessage;
import com.leone.pay.common.exception.ValidateException;
import com.leone.pay.common.property.AppProperties;
import com.leone.pay.entity.Order;
import com.leone.pay.entity.OrderBill;
import com.leone.pay.service.OrderBillService;
import com.leone.pay.service.OrderService;
import com.leone.pay.service.UserService;
import com.leone.pay.utils.PaymentUtils;
import com.leone.pay.utils.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;

/**
 * <p> 支付宝支付
 *
 * @author leone
 * @since 2018-05-27
 **/
@Slf4j
@Service
public class AliPayService {

    @Resource
    private OrderService orderService;

    @Resource
    private UserService userService;

    @Resource
    private AppProperties appProperties;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private OrderBillService orderBillService;

    // 阿里 sdk 封装
    private AlipayClient alipayClient;

    @PostConstruct
    public void initMethod() {
        alipayClient = new DefaultAlipayClient(
                appProperties.getAli().getServer_url(),
                appProperties.getAli().getApp_id(),
                appProperties.getAli().getAlipay_private_key(),
                appProperties.getAli().getFormat(),
                appProperties.getAli().getCharset(),
                appProperties.getAli().getAlipay_public_key(),
                appProperties.getAli().getSign_type());
    }

    /**
     * 支付宝提现
     *
     * @param orderId
     */
    public Result deposit(Long orderId) {
        // 校验订单信息
        Order order = orderService.findOne(orderId);
        if (order.getStatus() != OrderStatus.CREATE.getStatus()) {
            log.error(ExceptionMessage.ORDER_STATUS_INCORRECTNESS + " orderId: {}", orderId);
            throw new ValidateException(ExceptionMessage.ORDER_STATUS_INCORRECTNESS);
        }

        AlipayFundTransToaccountTransferModel transferModel = new AlipayFundTransToaccountTransferModel();

        transferModel.setOutBizNo(1 + RandomUtil.randomNum(15));
        transferModel.setAmount(order.getTotalFee().toString());
        transferModel.setPayeeAccount("real account");
        transferModel.setPayeeRealName("real name");
        transferModel.setPayerShowName("from name");
        transferModel.setRemark("remark");
        transferModel.setPayeeType("ALIPAY_LOGONID");
        try {
            AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
            request.setBizModel(transferModel);
            AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            log.info("ali deposit error message:{}", e.getMessage());
            return Result.success(ExceptionMessage.ALI_DEPOSIT_SUCCESS);
        }
        return Result.error(ExceptionMessage.ALI_DEPOSIT_FAILED);
    }


    /**
     * 支付宝扫码支付生成二维码响应到浏览器
     *
     * @param orderId
     * @param response
     * @return
     */
    public Result aliQrCodePay(Long orderId, HttpServletResponse response) throws Exception {
        // 校验订单信息
        Order order = orderService.findOne(orderId);
        if (order.getStatus() != OrderStatus.CREATE.getStatus()) {
            log.error(ExceptionMessage.ORDER_STATUS_INCORRECTNESS + " orderId: {}", orderId);
            throw new ValidateException(ExceptionMessage.ORDER_STATUS_INCORRECTNESS);
        }

        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
        Map<String, String> params = new TreeMap<>();
        params.put("out_trade_no", order.getOutTradeNo());
        params.put("total_amount", order.getTotalFee().toString());
        params.put("subject", "备注");
        params.put("body", "详情");
        params.put("store_id", "NJ_2031");
        params.put("timeout_express", "90m");
        request.setBizContent(objectMapper.writeValueAsString(params));
        request.setNotifyUrl(appProperties.getAli().getNotify_url());

        AlipayTradePrecreateResponse responseData = alipayClient.execute(request);
        log.info("response:{}", responseData.getBody());
        String qrCode = responseData.getQrCode();
        if (!ObjectUtils.isEmpty(qrCode)) {
            PaymentUtils.createQRCode(qrCode, response);
            return Result.success(ExceptionMessage.SUCCESS);
        }
        return Result.error(ExceptionMessage.ALI_PAY_CREATE_QR_CODE_SUCCESS);
    }

    /**
     * 支付宝退款
     *
     * @param orderId
     * @param servletRequest
     * @return
     */
    public Result aliRefund(Long orderId, HttpServletRequest servletRequest) throws Exception {
        // 校验订单信息
        Order order = orderService.findOne(orderId);
        if (order.getStatus() < OrderStatus.PAY.getStatus()) {
            log.error(ExceptionMessage.ORDER_STATUS_INCORRECTNESS + " orderId: {}", orderId);
            throw new ValidateException(ExceptionMessage.ORDER_STATUS_INCORRECTNESS);
        }
        // 创建退款请求builder,设置请求参数
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        Map<String, String> params = new TreeMap<>();
        // 必须 商户订单号
        params.put("out_trade_no", order.getOutTradeNo());
        // 必须 支付宝交易号
        params.put("trade_no", order.getTotalFee().toString());
        // 必须 退款金额
        params.put("refund_amount", order.getTotalFee().toString());
        // 可选 代表 退款的原因说明
        params.put("refund_reason", "退款的原因说明");
        // 可选 标识一次退款请求,同一笔交易多次退款需要保证唯一(就是out_request_no在2次退款一笔交易时,要不一样),如需部分退款,则此参数必传
        params.put("out_request_no", 1 + RandomUtil.randomNum(11));
        // 可选 代表 商户的门店编号
        params.put("store_id", "90m");
        request.setBizContent(objectMapper.writeValueAsString(params));
        AlipayTradeRefundResponse responseData = alipayClient.execute(request);
        if (responseData.isSuccess()) {
            log.info("ali refund success tradeNo:{}", order.getOutTradeNo());
            return Result.success(ExceptionMessage.SUCCESS);
        }
        log.info("ali refund failed tradeNo:{}", order.getOutTradeNo());
        return Result.error(ExceptionMessage.ALI_PAY_REFUND_FAILED);
    }


    /**
     * 阿里pc支付
     *
     * @param orderId
     * @param servletRequest
     * @return
     */
    public String aliPcPay(Long orderId, HttpServletRequest servletRequest) throws Exception {
        Order order = orderService.findOne(orderId);
        AlipayTradePagePayRequest payRequest = new AlipayTradePagePayRequest();
        // 前台通知
        payRequest.setReturnUrl(appProperties.getAli().getReturn_url());
        // 后台回调
        payRequest.setNotifyUrl(appProperties.getAli().getNotify_url());
        Map<String, String> params = new TreeMap<>();
        params.put("out_trade_no", order.getOutTradeNo());
        // 订单金额:元
        params.put("total_amount", order.getTotalFee().toString());
        params.put("subject", "订单标题");
        // 实际收款账号,一般填写商户PID即可
        params.put("seller_id", appProperties.getAli().getMch_id());
        // 电脑网站支付
        params.put("product_code", "FAST_INSTANT_TRADE_PAY");
        params.put("body", "两个橘子");
        payRequest.setBizContent(objectMapper.writeValueAsString(params));
        log.info("业务参数:" + payRequest.getBizContent());
        String result = ExceptionMessage.ERROR.getMessage();
        try {
            result = alipayClient.pageExecute(payRequest).getBody();
        } catch (AlipayApiException e) {
            log.error("ali pay error message:{}", e.getMessage());
        }
        return result;
    }

    /**
     * 支付宝App支付
     *
     * @param orderId
     * @param servletRequest
     * @return
     */
    public String appPay(Long orderId, HttpServletRequest servletRequest) {
        Order order = orderService.findOne(orderId);
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        // SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("描述");
        model.setSubject("商品名称");
        model.setOutTradeNo(order.getOutTradeNo());
        model.setTimeoutExpress("30m");
        model.setTotalAmount(order.getTotalFee().toString());
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl(appProperties.getAli().getNotify_url());
        try {
            // 这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            //就是orderString 可以直接给客户端请求,无需再做处理。
            log.info("orderString:{}", response.getBody());
            return response.getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 支付回调通知
     *
     * @param request
     * @param response
     */
    public void aliNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String requestXml = PaymentUtils.getRequestData(request);
        Map requestMap = PaymentUtils.xmlToMap(requestXml);
        Assert.notNull(requestMap, ExceptionMessage.XML_DATA_INCORRECTNESS.getMessage());

        // 当返回的return_code为SUCCESS则回调成功
        if (requestMap.get("code").equals(10000)) {

            // TODO 保存订单流水 具体细节待实现
            orderBillService.save(new OrderBill());
            log.info("notify success");
        } else {
            log.error("notify failed");
        }
    }
}