package com.nuliji.tools.pay;

import com.alibaba.fastjson.JSON;
import com.nuliji.tools.pay.common.PayInfo;
import com.nuliji.tools.pay.common.ResultInfo;
import com.tencent.WXPay;
import com.tencent.common.Signature;
import com.tencent.common.Util;
import com.tencent.protocol.ResData;
import com.tencent.protocol.pay_protocol.*;
import com.tencent.protocol.pay_query_protocol.ScanPayQueryReqData;
import com.tencent.protocol.pay_query_protocol.ScanPayQueryResData;
import com.tencent.service.ScanPayQueryService;
import com.tencent.service.UnifiedOrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;

/**
 * Created by gaojie on 2017/5/10.
 */
public class WechatPay implements PaySource {
    //添加一个日志器
    private static final Logger logger = LoggerFactory.getLogger(WechatPay.class);

    private String appId = "";
    private String appKey = "";
    private String pid = "";

    public WechatPay(String appId, String appKey, String pid){
        this.appId = appId;
        this.appKey = appKey;
        this.pid = pid;

        WXPay.initSDKConfiguration(appKey,
                appId,
                pid,
                "","",""
        );
    }

    @Override
    public ResultInfo notifyTrade(HttpServletRequest request) throws Exception {
        InputStream is = request.getInputStream();
        BufferedReader br = request.getReader();

        String str;
        StringBuilder content = new StringBuilder();
        while((str = br.readLine()) != null){
            content.append(str);
        }
        logger.debug("notify:"+content);
        // 支付完成才会发送异步通知
        NoticeResData noticeResData = (NoticeResData) Util.getObjectFromXML(content.toString(), NoticeResData.class);

        String sign = noticeResData.getSign();
        noticeResData.setSign("");
        boolean flag = checkSign(noticeResData.toMap(), sign);
        ResultInfo info = new ResultInfo();
        info.setResponse(noticeResData);
        if(!flag){
            info.setStatus("system");
            info.setMessage("签名校验错误");
            return info;
        }
        info = parseResult(noticeResData, "success");
        info.setMessage("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
        return info;
    }

    @Override
    public ResultInfo returnTrade(ResultInfo info) {
        String response = info.getResponse().toString();
        if(response.indexOf("get_brand_wcpay_request") == 0){
            // wap返回
            switch(response){
                case "get_brand_wcpay_request:ok":
                    info.setStatus("wait");
                    break;
                case "get_brand_wcpay_request:fail":
                    info.setStatus("system");
                    break;
                case "get_brand_wcpay_request:cancel":
                    info.setStatus("system");
                    break;
                default:
                    info.setStatus("system");
            }
        }else{
            // app返回
            switch(response){
                case "0":
                    info.setStatus("wait");
                    break;
                case "-1":
                    info.setStatus("system");
                    break;
                case "-2":
                    info.setStatus("system");
                    break;
                default:
                    info.setStatus("system");
            }
        }
        return info;
    }

    @Override
    public boolean validTrade(ResultInfo info, PayInfo payInfo) {
        if(!info.getAppId().equals(payInfo.getAppId())) return false;
        if(!info.getPid().equals(payInfo.getPid())) return false;

        return true;
    }

    public PayInfo appTrade(String payNo, String pid, BigDecimal money, String subject, String body, String notifyUrl, String ip) throws Exception{
        UnifiedOrderReqData unifiedOrderReqData = new UnifiedOrderReqData(body, "", payNo, (int)(money.floatValue() * 100), "", ip, "", "", "", notifyUrl, "APP");
        //接受API返回
        String payServiceResponseString;
        UnifiedOrderService unifiedOrderService = new UnifiedOrderService();
        payServiceResponseString = unifiedOrderService.request(unifiedOrderReqData);

        //将从API返回的XML数据映射到Java对象
        UnifiedOrderResData unifiedOrderResData = (UnifiedOrderResData) Util.getObjectFromXML(payServiceResponseString, UnifiedOrderResData.class);
        logger.debug(JSON.toJSONString(unifiedOrderResData));

        PayInfo payInfo = new PayInfo();
        payInfo.setPid(pid);
        payInfo.setAppId(appId);
        payInfo.setMoney(money);
        String prepayid = unifiedOrderResData.getPrepay_id();
        if(prepayid!=null){
            AppReqData appReqData = new AppReqData(prepayid);
            payInfo.setRequest(appReqData);
            payInfo.setStatus("ok");
        }else{
            payInfo.setStatus("fail");
            payInfo.setRequest(unifiedOrderResData);
        }

        return payInfo;
    }

    public PayInfo preTrade(String payNo, String pid, BigDecimal money, String subject, String body, String notifyUrl, String ip) throws Exception{
        UnifiedOrderReqData unifiedOrderReqData = new UnifiedOrderReqData(body, "", payNo, (int)(money.floatValue() * 100), "", ip, "", "", "", notifyUrl, "NATIVE");
        //接受API返回
        String payServiceResponseString;
        UnifiedOrderService unifiedOrderService = new UnifiedOrderService();
        payServiceResponseString = unifiedOrderService.request(unifiedOrderReqData);

        //将从API返回的XML数据映射到Java对象
        UnifiedOrderResData unifiedOrderResData = (UnifiedOrderResData) Util.getObjectFromXML(payServiceResponseString, UnifiedOrderResData.class);
        logger.debug(JSON.toJSONString(unifiedOrderResData));

        PayInfo payInfo = new PayInfo();
        payInfo.setPid(pid);
        payInfo.setAppId(appId);
        payInfo.setMoney(money);
        String code_url = unifiedOrderResData.getCode_url();
        if(code_url!=null){
            payInfo.setRequest(code_url);
            payInfo.setStatus("ok");
        }else{
            payInfo.setStatus("fail");
            payInfo.setRequest(unifiedOrderResData);
        }

        return payInfo;
    }

    public PayInfo jsTrade(String payNo, String pid, BigDecimal money, String subject, String body, String notifyUrl, String ip) throws Exception{
        UnifiedOrderReqData unifiedOrderReqData = new UnifiedOrderReqData(body, "", payNo, (int)(money.floatValue() * 100), "", ip, "", "", "", notifyUrl, "JSAPI");
        //接受API返回
        String payServiceResponseString;
        UnifiedOrderService unifiedOrderService = new UnifiedOrderService();
        payServiceResponseString = unifiedOrderService.request(unifiedOrderReqData);

        //将从API返回的XML数据映射到Java对象
        UnifiedOrderResData unifiedOrderResData = (UnifiedOrderResData) Util.getObjectFromXML(payServiceResponseString, UnifiedOrderResData.class);
        logger.debug(JSON.toJSONString(unifiedOrderResData));

        PayInfo payInfo = new PayInfo();
        payInfo.setPid(pid);
        payInfo.setAppId(appId);
        payInfo.setMoney(money);
        String prepayid = unifiedOrderResData.getPrepay_id();
        if(prepayid!=null){
            JsReqData jsReqData = new JsReqData(prepayid);
            payInfo.setRequest(jsReqData);
            payInfo.setStatus("ok");
        }else{
            payInfo.setStatus("fail");
            payInfo.setRequest(unifiedOrderResData);
        }

        return payInfo;
    }
    @Override
    public ResultInfo getTrade(String payNo, String transactionNo, String pid) throws Exception{
        String payQueryServiceResponseString;

        ScanPayQueryService scanPayQueryService = new ScanPayQueryService();

        ScanPayQueryReqData scanPayQueryReqData = new ScanPayQueryReqData("", payNo);
        payQueryServiceResponseString = scanPayQueryService.request(scanPayQueryReqData);

        logger.debug("getTrade:"+payQueryServiceResponseString);

        //将从API返回的XML数据映射到Java对象
        ScanPayQueryResData scanPayQueryResData = (ScanPayQueryResData) Util.getObjectFromXML(payQueryServiceResponseString, ScanPayQueryResData.class);

        ResultInfo info = parseResult(scanPayQueryResData, "ok");
        if(info.getStatus().equals("ok")){
            /*
             SUCCESS—支付成功
             REFUND—转入退款
             NOTPAY—未支付
             CLOSED—已关闭
             REVOKED—已撤销(刷卡支付)
             USERPAYING--用户支付中
             PAYERROR--支付失败(其他原因，如银行返回失败)
             */
            switch(scanPayQueryResData.getTrade_state()){
                case "SUCCESS":
                    info.setStatus("success");
                    break;
                case "REFUND":
                    info.setStatus("refund");
                    break;
                case "NOTPAY":
                    info.setStatus("wait");
                    break;
                case "REVOKED":
                    info.setStatus("cancel");
                    break;
                case "USERPAYING":
                    info.setStatus("wait");
                    break;
                case "PAYERROR":
                    info.setStatus("error");
                    break;
            }
        }
        return info;
    }

    private ResultInfo parseResult(ResData response, String status){
        ResultInfo info = new ResultInfo();
        info.setAppId(response.getAppid());
        info.setPid(response.getMch_id());
        info.setResponse(response);
        info.setNo(response.getOut_trade_no());
        info.setMoney(BigDecimal.valueOf(Float.valueOf(response.getTotal_fee())/100));
        info.setOpenId(response.getOpenid());
        info.setTransaction_no(response.getTransaction_id());

        if (response == null || response.getReturn_code() == null) {
            return formatInfo(info, "system", "请求错误");
        }

        if (response.getReturn_code().equals("FAIL")) {
            return formatInfo(info, "system", response.getReturn_msg());
        } else {
            if (response.getResult_code().equals("SUCCESS")) {//业务层成功
                return formatInfo(info, status, "");
            } else {
                return formatInfo(info, "error", response.getErr_code_des());
            }
        }
    }

    private ResultInfo formatInfo(ResultInfo info, String status, String message){
        info.setStatus(status);
        info.setMessage(message);
        return info;
    }

    private boolean checkSign(Map param, String sign){
        String signNow = Signature.getSign(param);
        logger.debug(sign);
        return Objects.equals(signNow.trim(), sign.trim());
    }
}
