package com.jeequan.jeepay.thirdparty.channel.kqpay;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bill99.crypto.entity.Bill99CertConfig;
import com.bill99.crypto.service.CryptoService;
import com.bill99.crypto.service.processor.P7CryptoServiceProcessor;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvParams;
import com.jeequan.jeepay.core.model.params.kqpay.KqpayIsvsubMchParams;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayReqHead;
import com.jeequan.jeepay.thirdparty.channel.kqpay.model.KqpayResp;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.HttpClientCert;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.HttpsClientFactory;
import com.jeequan.jeepay.thirdparty.channel.kqpay.util.KqpayConst;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import com.jeequan.jeepay.thirdparty.util.ChannelCertConfigKitBean;
import lombok.AccessLevel;
import lombok.Cleanup;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
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.util.Assert;

import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;

/**
 * 快钱请求参数
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class KqpayKit {

    private static final CryptoService cryptoService = P7CryptoServiceProcessor.getInstance();

    /**
     * 快钱入网请求网关
     */
    public static final String APPLYMENTS_GATE_WAY = KqpayConst.APPLYMENTS_GATE_WAY;

    public static final String TRADE_GATE_WAY = KqpayConst.TRADE_GATE_WAY;


    public static String getExternalRefNumber(String memberCode) {
        return memberCode + DateUtil.format(new Date(), DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
    }

    public static String uploadRequest(String imgUrl, KqpayIsvParams isvParams) {
        byte[] imgFileByteArray = HttpUtil.downloadBytes(imgUrl);
        String base64 = Base64.encode(imgFileByteArray);

        JSONObject reqParam = new JSONObject();
        reqParam.put("fileBuffer", base64);
        reqParam.put("fileName", "test.jpg");

        return KqpayKit.applymentRequest(KqpayConst.ReqMethod.BS000, isvParams, reqParam).getResponseBody().getString("fssId");
    }

    public static KqpayResp applymentRequest(KqpayConst.ReqMethod reqMethod, KqpayIsvParams isvParams, JSONObject bizContent) {
        String externalRefNumber = ((String) bizContent.remove("externalRefNumber"));
        ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
        Assert.notNull(configContextQueryService, "无法获取快钱入网配置信息");

        KqpayReqHead head = init(reqMethod, isvParams, externalRefNumber);
        head.setVendorMemberCode(null);
        try {
            return reqApi(APPLYMENTS_GATE_WAY, head, bizContent, isvParams);
        } catch (Exception e) {
            log.info("快钱入网请求异常,", e);
            throw new BizException("快钱入网请求异常, " + e.getMessage());
        }
    }

    /**
     * 初始化
     *
     * @return
     */
    public static KqpayReqHead init(KqpayConst.ReqMethod reqMethod, KqpayIsvParams isvParams, String outFlowNo) {
        initCert(isvParams);
        KqpayReqHead head = new KqpayReqHead();
        head.setVersion("1.0.0");
        head.setVendorMemberCode(isvParams.getVendorMemberCode());
        head.setMemberCode(head.getVendorMemberCode());
        head.setMessageType(reqMethod.getMessageType());
        if (StringUtils.isNotEmpty(outFlowNo)) {
            head.setExternalRefNumber(outFlowNo);
        } else {
            head.setExternalRefNumber(getExternalRefNumber(isvParams.getVendorMemberCode()));
        }
        return head;
    }


    public static KqpayResp payRequest(KqpayConst.ReqMethod reqMethod, MchAppConfigContext mchAppConfigContext, JSONObject bizContent, String outFlowNo) {// 配置参数
        ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
        Assert.notNull(configContextQueryService, "无法获取快钱入网配置信息");
        KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.KQPAY);

        KqpayReqHead head = init(reqMethod, isvParams, outFlowNo);
        head.setMemberCode(bizContent.getString("kqCode"));
        try {
            return reqApi(TRADE_GATE_WAY, head, bizContent, isvParams);
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 设置参数
     *
     * @param mchAppConfigContext
     * @param bizContent
     */
    public static void setPayParams(MchAppConfigContext mchAppConfigContext, JSONObject bizContent) {
        KqpayIsvsubMchParams mchParams = JSON.parseObject(mchAppConfigContext.getMchApplyment().getSuccResParameter(),KqpayIsvsubMchParams.class);
        bizContent.put("merchantId", mchParams.getSubMerchantId());
        List<JSONObject> terminalInfos = mchParams.getTerminalInfos();
        JSONObject item = terminalInfos.stream().filter(e -> "terminalQkf".equals(e.getString("type"))).findFirst().orElse(new JSONObject());
        JSONArray terminalIds = item.getJSONArray("terminalIds");
        bizContent.put("terminalId", terminalIds.get(0));
        bizContent.put("kqCode", mchParams.getSubMemberCode());
        bizContent.put("cur", "CNY");
    }

    public static KqpayResp reqApi(String url, KqpayReqHead head, JSONObject bizData, KqpayIsvParams isvParams) throws Exception {
        JSONObject encryptBody = null;
        try {
            encryptBody = cryptoService.seal(bizData.toString());
        } catch (Exception e) {
            log.error("请求快钱，加密加签出错。", e);
        }
        JSONObject requestString = new JSONObject();
        requestString.put("head", head);
        requestString.put("requestBody", encryptBody);
        log.info("快钱接口请求地址：{}", url);
        log.info("快钱接口请求业务参数：{}", bizData);
        log.info("快钱接口请求参数：{}", requestString);
        @Cleanup CloseableHttpClient httpClient = url.startsWith("https") ? createHttpsClient(isvParams) : HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(60000)
                .setConnectTimeout(10000).build();
        httpPost.setConfig(requestConfig);
        httpPost.setEntity(new StringEntity(requestString.toJSONString(), Consts.UTF_8));
        return httpClient.execute(httpPost, httpresponse -> {
            int statusCode = httpresponse.getStatusLine().getStatusCode();
            if (200 != statusCode) {
                return null;
            } else {
                String entityStr;
                HttpEntity entity = httpresponse.getEntity();
                if (null != entity) {
                    entityStr = EntityUtils.toString(entity, Consts.UTF_8);
                    log.info("快钱请求返回待解密报文：{}", entityStr);
                    JSONObject respEncryptObject = JSON.parseObject(entityStr);
                    JSONObject respHead = respEncryptObject.getJSONObject("head");
                    JSONObject respEncryptBody = respEncryptObject.getJSONObject("responseBody");
                    String respSignedData = respEncryptBody.getString("signedData");
                    String respEnvelopedData = respEncryptBody.getString("envelopedData");
                    KqpayResp kqpayResp = new KqpayResp(respHead);
                    if (KqpayConst.SUCCESS.equals(respHead.getString("responseCode"))) {
                        String respDecryptBody = null;
                        try {
                            respDecryptBody = cryptoService.unSeal(respEnvelopedData, respSignedData);
                            log.info("快钱请求返回报文：{}", respDecryptBody);
                        } catch (Exception e) {
                            log.info("快钱入网解密异常", e);
                        }
                        kqpayResp.setResponseBody(JSON.parseObject(respDecryptBody));
                        return kqpayResp;
                    } else {
                        log.error("请求快钱出错, head报错信息 = {}", respHead.toJSONString());
                        throw new BizException(respHead.getString("responseTextMessage"));
                    }
                } else {
                    throw new BizException("接口请求异常");
                }
            }
        });
    }

    private static CloseableHttpClient createHttpsClient(KqpayIsvParams kqpayIsvParams) throws Exception {
        ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
        String certFilePath = channelCertConfigKitBean.getCertFilePath(kqpayIsvParams.getSslPublicPath());

        @Cleanup InputStream keystoreFileInputStream = new FileInputStream(certFilePath);

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(keystoreFileInputStream, kqpayIsvParams.getStorePwd().toCharArray());
        HttpClientCert httpClientCert = new HttpClientCert();
        httpClientCert.setKeyStore(keyStore);
        httpClientCert.setKeyStorePwd(kqpayIsvParams.getStorePwd());
        httpClientCert.setConnTimeout(60000);
        httpClientCert.setSoTimeout(10000);
        httpClientCert.setSSLVersion("TLSv1.2");
        HttpsClientFactory factory = new HttpsClientFactory();
        return factory.createSSLClient(httpClientCert);
    }


    /**
     * 解析异步通知参数
     *
     * @param resNotify
     * @return
     */
    public static boolean checkResultNotify(JSONObject resNotify, KqpayIsvParams isvParams) {
        try {
            initCert(isvParams);
            JSONObject requestBody = resNotify.getJSONObject("requestBody");
            String signedData = requestBody.getString("signedData");
            String envelopedData = requestBody.getString("envelopedData");
            String data = cryptoService.unSeal(envelopedData, signedData);
            resNotify.put("requestBody", JSON.parseObject(data));
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 解析异步通知参数
     *
     * @param resNotify
     * @return
     */
    public static boolean checkResultNotify(JSONObject resNotify, MchAppConfigContext mchAppConfigContext) {
        try {
            initCert(mchAppConfigContext);
            JSONObject requestBody = resNotify.getJSONObject("requestBody");
            String signedData = requestBody.getString("signedData");
            String envelopedData = requestBody.getString("envelopedData");
            String data = cryptoService.unSeal(envelopedData, signedData);
            resNotify.put("requestBody", JSON.parseObject(data));
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 组装快钱异步通知返回参数
     *
     * @param notifyParams
     * @return
     */
    public static String resSuccess(JSONObject notifyParams) {
        JSONObject responseBody = new JSONObject(1);
        responseBody.put("isReceived", "1");
        try {
            JSONObject respEncryptBody = cryptoService.seal(responseBody.toJSONString());
            JSONObject respMessage = new JSONObject();
            respMessage.put("head", notifyParams.getJSONObject("head"));
            respMessage.put("responseBody", respEncryptBody);
            return StringEscapeUtils.unescapeJson(respMessage.toJSONString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "error";
    }

    public static KqpayIsvParams initCert(MchAppConfigContext configContext) {
        ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
        Assert.notNull(configContextQueryService, "无法获取快钱入网配置信息");
        KqpayIsvParams isvParams = (KqpayIsvParams) configContextQueryService.queryIsvParams(configContext.getMchApplyment().getIsvNo(), CS.IF_CODE.KQPAY);
        return initCert(isvParams);
    }

    public static KqpayIsvParams initCert(KqpayIsvParams isvParams) {
        ChannelCertConfigKitBean channelCertConfigKitBean = SpringBeansUtil.getBean(ChannelCertConfigKitBean.class);
        Assert.notNull(channelCertConfigKitBean, "无法获取快钱入网配置文件");
        String bill99DefaultPublicPath = channelCertConfigKitBean.getCertFilePath(isvParams.getBill99DefaultPublicPath());
        String merchantDefaultPrivatePath = channelCertConfigKitBean.getCertFilePath(isvParams.getMerchantDefaultPrivatePath());

        Bill99CertConfig bill99CertConfig = new Bill99CertConfig();
        bill99CertConfig.setBill99DefaultPublicPath(bill99DefaultPublicPath);
        bill99CertConfig.setMerchantDefaultPrivatePassword(isvParams.getMerchantDefaultPrivatePassword());
        bill99CertConfig.setMerchantDefaultPrivatePath(merchantDefaultPrivatePath);
        cryptoService.setBill99CertConfig(bill99CertConfig);
        return isvParams;
    }
}
