package com.yida.data.common.core.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.yida.data.common.core.entity.apply.EduApplyProductOrder;
import com.yida.data.common.core.entity.constant.WxPayConstant;
import com.yida.data.common.core.entity.system.EduPayWxConfig;
import com.yida.data.common.core.exception.FebsException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

@Slf4j
public class WxPayUtil {

    private static final String WX_CALL = "/product/in/productOrder/refund";

    /**
     * 构建请求客户端
     *
     * @param config
     * @return
     */
    public static CloseableHttpClient getClient(EduPayWxConfig config) {
        CloseableHttpClient httpClient;
        // 商户私钥
        PrivateKey privateKey = null;
        AutoUpdateCertificatesVerifier verifier = null;
        AesUtil aesUtil;
        try {
            privateKey =
                PemUtil.loadPrivateKey(new ByteArrayInputStream(config.getApiCreditPrivateKey().getBytes("utf-8")));
            //aesUtil = new AesUtil(config.getApiV3Key().getBytes("utf-8"));
            verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(config.getMchId(),
                    new PrivateKeySigner(config.getApiCreditSerial(),
                        privateKey)),
                config.getApiV3Key().getBytes("utf-8"));
        } catch (UnsupportedEncodingException exception) {
        }
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
            .withMerchant(config.getMchId(), config.getApiCreditSerial(), privateKey)
            .withValidator(new WechatPay2Validator(verifier));
        return builder.build();
    }

    public static AesUtil getAesUtil(EduPayWxConfig config) {
        try {
            return new AesUtil(config.getApiV3Key().getBytes("utf-8"));
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * h5下单
     */
    public static String placeOrderH5(EduPayWxConfig config, EduApplyProductOrder order, String clientIp,
        String notifyUrl) throws IOException {
        JSONObject params = new JSONObject();
        // 商户参数
        params.set("appid", config.getWxPublicId());
        params.set("mchid", config.getMchId());
        // 商品描述
        params.set("description", order.getProductName());
        // 商户侧唯一订单号
        params.set("out_trade_no", order.getOrderCode());
        // 交易结束时间
        params.set("time_expire", ZonedDateTime.now().plusMinutes(15L).format(DateTimeFormatter.ofPattern("yyyy-MM" +
            "-dd'T'HH:mm:ssXXX")));
        // 通知地址
        params.set("notify_url", notifyUrl);
        // 订单金额
        params.set("amount", new JSONObject() {{
            // 订单总金额
            set("total", order.getPraccticalMoney() * 100);
            // 货币类型，境内商户号仅支持人民币
            set("currency", "CNY");
        }});
        // 场景信息
        params.set("scene_info", new JSONObject() {{
            // 用户终端ip
            set("payer_client_ip", clientIp);
            // h5场景信息
            set("h5_info", new JSONObject() {{
                // 场景类型
                set("type", "Wap");
            }});
        }});
        HttpPost httpPost = new HttpPost(WxPayConstant.UFDODER_URL + "h5");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(JSONUtil.toJsonStr(params), "UTF-8"));
        CloseableHttpResponse response = getClient(config).execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            String respBody = EntityUtils.toString(response.getEntity()).toString();
            if (statusCode == 200) {
                return JSONUtil.parseObj(respBody).get("h5_url").toString();
            } else if (statusCode == 204) {
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode + ",return body = " + respBody);
                throw new IOException("request failed");
            }
        } catch (Exception exception) {

            throw new FebsException("下单失败");
        }
        return null;
    }

    /**
     * jsapi下单
     */
    public static String placeOrderJs(EduPayWxConfig config, EduApplyProductOrder order, String openId, String clientIp,
        String notifyUrl) throws IOException {
        JSONObject params = new JSONObject();
        // 商户参数
        params.set("appid", config.getWxPublicId());
        params.set("mchid", config.getMchId());
        // 商品描述
        params.set("description", order.getProductName());
        // 商户侧唯一订单号
        params.set("out_trade_no", order.getOrderCode());
        // 交易结束时间
        params.set("time_expire", ZonedDateTime.now().plusMinutes(15L).format(DateTimeFormatter.ofPattern("yyyy-MM" +
            "-dd'T'HH:mm:ssXXX")));
        // 通知地址
        params.set("notify_url", notifyUrl);
        //log.info("notify_url:[{}]", apiUrl + wxCall);
        // 订单金额
        params.set("amount", new JSONObject() {{
            // 订单总金额 单位为分
            set("total", (long) (order.getPraccticalMoney() * 100));
            // 货币类型，境内商户号仅支持人民币
            set("currency", "CNY");
        }});
        // 支付者信息
        params.set("payer", new JSONObject() {{
            // 用户再商户appid下的唯一标识
            set("openid", openId);
        }});
        // 场景信息
        params.set("scene_info", new JSONObject() {{
            // 用户终端ip
            set("payer_client_ip", clientIp);
        }});
        HttpPost httpPost = new HttpPost(WxPayConstant.UFDODER_URL + "jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(JSONUtil.toJsonStr(params), "UTF-8"));
        CloseableHttpResponse response = getClient(config).execute(httpPost);
        //try {
        int statusCode = response.getStatusLine().getStatusCode();
        String respBody = EntityUtils.toString(response.getEntity());
        if (statusCode == 200) {
            return JSONUtil.parseObj(respBody).get("prepay_id").toString();
        } else if (statusCode == 204) {
            log.error("微信js下单失败,res:[{}],params:[{}]", respBody, params);
        } else {
            log.error("微信js下单失败,res:[{}],params:[{}]", respBody, params);
            throw new FebsException("下单失败");
        }
        //} catch (Exception exception) {
        //    log.error("placeJsOrder fail,msg:[{}],stacktrace:[{}]", exception.getMessage(),
        //            exception.getStackTrace());
        //}
        return null;
    }

    /**
     * 查单
     */
    public static String queryOrder(EduPayWxConfig config, String orderCode) throws URISyntaxException, IOException {
        URIBuilder uriBuilder =
            new URIBuilder(WxPayConstant.ORDER_QUERY + orderCode).addParameter("mchid",
                config.getMchId());
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader("Accept", "application/json");

        CloseableHttpResponse response = getClient(config).execute(httpGet);
        String respBody = EntityUtils.toString(response.getEntity());
        return respBody;
    }

    /**
     * 关单
     */
    public static void closeOrder(EduPayWxConfig config, String orderCode) throws UnsupportedEncodingException {
        HttpPost httpPost = new HttpPost(WxPayConstant.CLOSE_ORDER + orderCode + "/close");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        httpPost.setEntity(new StringEntity(new JSONObject() {{
            set("mchid", config.getMchId());
        }}.toString()));
        try {
            CloseableHttpResponse response = getClient(config).execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
        } catch (Exception e) {

        }
    }

    /**
     * 退款
     */
    public static void refund(EduPayWxConfig config, String orderCode, String reason, String refundCode, Long refund,
        Long total,
        String notifyUrl) throws IOException {
        JSONObject params = new JSONObject();
        // 商户订单号
        params.set("out_trade_no", orderCode);
        // 商户退款号
        params.set("out_refund_no", refundCode);
        // 退款结果回调
        params.set("notify_url", notifyUrl);
        // 退款原因
        params.set("reason", reason);
        // 金额信息来源
        params.set("amount", new JSONObject() {{
            // 退款金额
            set("refund", refund);
            // 原订单金额
            set("total", total);
            // 币种
            set("currency", "CNY");
        }});
        HttpPost httpPost = new HttpPost(WxPayConstant.REFUND);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(params.toString(), "UTF-8"));
        CloseableHttpResponse response = getClient(config).execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            String respBody = EntityUtils.toString(response.getEntity());
            if (statusCode == 200) {
                //return JSONUtil.parseObj(respBody).get("h5_url").toString();
            } else if (statusCode == 204) {
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode + ",return body = " + respBody);
                throw new IOException("request failed");
            }
        } catch (Exception exception) {

            throw new FebsException("退款失败");
        }
    }

    /**
     * 退款查询
     */
    public static String queryRefund(EduPayWxConfig config, String refundCode) {
        try {
            URIBuilder uriBuilder =
                new URIBuilder(WxPayConstant.REFUND_QUERY + refundCode);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");

            CloseableHttpResponse response = getClient(config).execute(httpGet);
            String respBody = EntityUtils.toString(response.getEntity());
            return respBody;
        } catch (Exception exception) {

        }
        return null;
    }

    /**
     * aes解密
     */
    public static String decodeText(EduPayWxConfig config, String associatedData, String nonce, String ciphertext)
        throws GeneralSecurityException, IOException {
        String decodeStr = getAesUtil(config).decryptToString(associatedData.getBytes(), nonce.getBytes(),
            ciphertext);
        return decodeStr;
    }


    /**
     * 申请交易账单(仅三个月内账单)
     *
     * @param date yyyy-MM-dd
     * @param type all:所有账单，SUCCESS：当日成功支付的账单，REFUND：当日退款账单
     * @return
     */
    public static String applyBill(EduPayWxConfig config, String date, String type) {
        if (StrUtil.isBlank(type)) {
            type = "ALL";
        }
        try {
            URIBuilder uriBuilder = new URIBuilder(WxPayConstant.BILL)
                .addParameter("bill_date", date)
                .addParameter("bill_type", type);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");

            CloseableHttpResponse response = getClient(config).execute(httpGet);
            Integer statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                return JSONUtil.parseObj(EntityUtils.toString(response.getEntity())).getStr("download_url");
            } else {
                throw new FebsException(String.format("下载对账文件错误,错误码:%d", statusCode));
            }
        } catch (URISyntaxException exception) {
            //log.error("applyBill fail,msg:[{}],stacktrace:[{}]", exception.getMessage(),
            //        exception.getStackTrace());
        } catch (ClientProtocolException exception) {
            //log.error("applyBill fail,msg:[{}],stacktrace:[{}]", exception.getMessage(), exception.getStackTrace());
        } catch (IOException exception) {
            //log.error("applyBill fail,msg:[{}],stacktrace:[{}]", exception.getMessage(), exception.getStackTrace());
        }
        return null;
    }
}
