package com.gree.mall.manager.logic.common;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gree.mall.manager.exception.RemoteServiceException;
import com.gree.mall.manager.plus.entity.AdminWebsit;
import com.gree.mall.manager.plus.service.AdminCompanyWechatService;
import com.gree.mall.manager.plus.service.AdminWebsitService;
import com.gree.mall.manager.utils.ArithUtils;
import com.gree.mall.manager.utils.ums.HttpUtils;
import com.gree.mall.manager.utils.ums.RSAUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

@Service
@Slf4j
public class UMSLogic {

    @Value("${ums.url}")
    private String url;
    @Value("${yue.huan.xin.url}")
    private String supplementUrl;
    @Resource
    AdminCompanyWechatService adminCompanyWechatService;
    @Resource
    AdminWebsitService adminWebsitService;

    /**
     * 云闪付退款
     */

    public void refund(String orderId, double totalFee, double refundFee, String websitId) throws Exception {
        final AdminWebsit websit = adminWebsitService.getById(websitId);
        JSONObject json = new JSONObject();

        json.put("requestTimestamp", DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));    // 报文请求时间
        json.put("mid", websit.getYunCompany()); // 商户号
        json.put("tid", websit.getYunNumber());    // 终端号
        json.put("merOrderId", websit.getYunSystem() + orderId); // 商户订单号
        json.put("refundAmount", (int) ArithUtils.mul(refundFee, 100));    // 退货的金额
        json.put("refundOrderId", getMerchantOrderId(websit.getYunSystem())); // 退款订单号

        //OPEN-BODY-SIG 方式
        String authorization = getOpenBodySig(websit.getYunAppid(), websit.getYunAppkey(), json.toString());
//        String authorization = AppletOrders.getOpenBodySig(websit.getUmsAppid(), websit.getUmsAppKey(), json.toString());
        String send = "";
        try {
            send = send(url + "/v1/netpay/refund", json.toString(), authorization);

        } catch (Exception e) {
            throw new RemoteServiceException("发送请求云闪付失败, 错误信息: " + e.getMessage());
        }

        if (!send.contains("成功")) {
            throw new RemoteServiceException("请求云闪付失败, 平台错误信息: " + send);
        }
    }

    /**
     * open-body-sig方式获取到Authorization 的值
     *
     * @param appId  f0ec96ad2c3848b5b810e7aadf369e2f
     * @param appKey 775481e2556e4564985f5439a5e6a277
     * @param body   json字符串 String body = "{\"merchantCode\":\"123456789900081\",\"terminalCode\":\"00810001\",\"merchantOrderId\":\"20123333644493200\",\"transactionAmount\":\"1\",\"merchantRemark\":\"测试\",\"payMode\":\"CODE_SCAN\",\"payCode\":\"285668667587422761\",\"transactionCurrencyCode\":\"156\"}";
     * @return
     * @throws Exception
     */
    private String getOpenBodySig(String appId, String appKey, String body) throws Exception {
        String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());   // eg:20190227113148
        String nonce = UUID.randomUUID().toString().replace("-", ""); // eg:be46cd581c9f46ecbd71b9858311ea12
        byte[] data = body.getBytes("UTF-8");
        log.info("ums data:\n" + body);
        InputStream is = new ByteArrayInputStream(data);
        String bodyDigest = DigestUtils.sha256Hex(is); // eg:d60bc3aedeb853e2a11c0c096baaf19954dd9b752e48dea8e919e5fb29a42a8d
        log.info("ums bodyDigest:\n" + bodyDigest);
        String str1_C = appId + timestamp + nonce + bodyDigest; // eg:f0ec96ad2c3848b5b810e7aadf369e2f + 20190227113148 + be46cd581c9f46ecbd71b9858311ea12 + d60bc3aedeb853e2a11c0c096baaf19954dd9b752e48dea8e919e5fb29a42a8d

        log.info("ums str1_C:" + str1_C);
        byte[] localSignature = hmacSHA256(str1_C.getBytes(), appKey.getBytes());

        String localSignatureStr = Base64.encodeBase64String(localSignature);   // Signature
        log.info("ums Authorization:\n" + "OPEN-BODY-SIG AppId=" + "\"" + appId + "\"" + ", Timestamp=" + "\"" + timestamp + "\"" + ", Nonce=" + "\"" + nonce + "\"" + ", Signature=" + "\"" + localSignatureStr + "\"\n");
        return ("OPEN-BODY-SIG AppId=" + "\"" + appId + "\"" + ", Timestamp=" + "\"" + timestamp + "\"" + ", Nonce=" + "\"" + nonce + "\"" + ", Signature=" + "\"" + localSignatureStr + "\"");
    }

    /**
     * @param data
     * @param key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    private byte[] hmacSHA256(byte[] data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
        String algorithm = "HmacSHA256";
        Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(key, algorithm));
        return mac.doFinal(data);
    }

    /**
     * 发送请求
     * @param url
     * @return
     * @throws Exception
     */
    private String send(String url, String entity , String authorization) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Authorization", authorization);
        StringEntity se = new StringEntity(entity, "UTF-8");
        se.setContentType("application/json");
        httpPost.setEntity(se);
        CloseableHttpResponse response = httpClient.execute(httpPost);
        HttpEntity entity1 = response.getEntity();
        String resStr = null;
        if (entity1 != null) {
            resStr = EntityUtils.toString(entity1, "UTF-8");
        }
        log.info("云闪付接口返回字符串: {}", resStr);
        httpClient.close();
        response.close();
        return resStr;
    }

    /**
     * 获取到订单号
     *
     * @return 订单号
     */
    public static String getMerchantOrderId(String code) {
        //需要以来源编号做前四位
        return code + org.apache.commons.lang.time.DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + RandomStringUtils.randomNumeric(7);
    }


    /**
     * 接收补贴申请补录资料流水
     */
    public JSONObject submitSubsidyApplyTrace(String userId, String reqSn, String encBizReqData, String servicePublicKey,
                                              String privateKey) throws Exception {
        String sourceUrl = supplementUrl + "/o2o/submitSubsidyApplTrace";
        log.info("准备请求{}", sourceUrl);
        return this.commonReqYueHuanXin(sourceUrl, userId, reqSn, encBizReqData, servicePublicKey, privateKey);
    }

    /**
     * 查询补贴申请补录资料流水
     */
    public JSONObject querySubsidyApplyTrace(String userId, String reqSn, String encBizReqData, String servicePublicKey,
                                             String privateKey) throws Exception {
        String sourceUrl = supplementUrl + "/o2o/querySubsidyApplTrace";
        log.info("准备请求{}", sourceUrl);
        return this.commonReqYueHuanXin(sourceUrl, userId, reqSn, encBizReqData, servicePublicKey, privateKey);
    }

    /**
     * 查询补贴申请补录资料流水
     */
    public JSONObject querySubsidyApply(String userId, String reqSn, String encBizReqData, String servicePublicKey,
                                        String privateKey) throws Exception {
        String sourceUrl = supplementUrl + "/o2o/querySubsidyAppl";
        log.info("准备请求{}", sourceUrl);
        return this.commonReqYueHuanXin(sourceUrl, userId, reqSn, encBizReqData, servicePublicKey, privateKey);
    }

    private JSONObject commonReqYueHuanXin(String sourceUrl, String userId, String reqSn, String data,
                                           String servicePublicKey, String privateKey) throws Exception {
        log.info("commonReqYueHuanXin请求data:{}", data);
        HashMap<String, Object> baseReq = new HashMap<>();
        baseReq.put("userId", userId);
        baseReq.put("reqSn", reqSn);
        baseReq.put("timestamp", DateUtil.formatDateTime(DateUtil.date()));
        byte[] encBizReqData = RSAUtils.encryptByPublicKey(data.getBytes(StandardCharsets.UTF_8), servicePublicKey);
        baseReq.put("encBizReqData", encBizReqData);
        baseReq.put("signAlg", "SHA256withRSA");
        baseReq.put("sign", RSAUtils.sign(encBizReqData, privateKey));

        String respBody = HttpUtils.post(sourceUrl,
                JSON.toJSONString(baseReq));
        JSONObject jsonObject = JSONObject.parseObject(respBody);

        if ("00000".equals(jsonObject.getString("code"))) {
            // 验签
            verifySign(jsonObject, servicePublicKey);

            // 解密
            byte[] decryptData = decryptByPrivateKey(jsonObject, privateKey);

            final String respData = new String(decryptData);

            log.info("commonReqYueHuanXin返回结果data:{}", respData);
            return JSON.parseObject(respData);
        }

        throw new RemoteServiceException("发起补贴申请流水：响应码：" + jsonObject.getString("code") + ",响应描述:" + jsonObject.getString("msg"));
    }

    /**
     * 验证签名
     *
     * @param respDTO
     */
    public static void verifySign(JSONObject respDTO, String serverPublicKey) throws Exception {
        try {
            boolean pass = RSAUtils.verifySign(Base64.decodeBase64((String) respDTO.get("encBizRespData")), (String) respDTO.get("sign"),
                    serverPublicKey);
            if (!pass) {
                throw new RuntimeException("业务请求数据验签不通过");
            }
        } catch (Exception e) {
            throw new Exception("业务请求数据验签失败");
        }
    }

    /**
     * 解密
     *
     * @param respDTO
     */
    public static byte[] decryptByPrivateKey(JSONObject respDTO, String privateKey) throws Exception {
        try {
            return RSAUtils.decryptByPrivateKey(Base64.decodeBase64((String) respDTO.get("encBizRespData")),
                    privateKey);
        } catch (Exception e) {
            throw new Exception("业务请求数据解密失败");
        }
    }
}



