package com.ruoyi.common.pay.yst.util;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.pay.yst.config.YstConfig;
import com.ruoyi.common.pay.yst.domain.YstRequest;
import com.ruoyi.common.pay.yst.domain.YstResponse;
import com.ruoyi.common.utils.ServiceUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 云商通请求客户端
 * @author wjh
 * 2024/8/1
 */
@Slf4j
public class YstClient {
    private static final String YYYY_MM_DD = "yyyyMMdd";
    private static final String HH_MM_SS = "HHmmss";

    private YstConfig config;

    private final PrivateKey privateKey;

    private final PublicKey tlPublicKey;

    public YstClient(YstConfig config) throws Exception
    {
        this.config = config;
        this.privateKey = SM2Utils.privKeySM2FromBase64Str(config.getPrivateKey());
        this.tlPublicKey = SM2Utils.pubKeySM2FromBase64Str(config.getPublicKey());
    }

    /**
     * 发送请求
     */
    public <T> YstResponse<T> sendRequest(String api, String transCode, Object param, Class<T> type) {
        return this.sendRequest(api, transCode, JSON.parseObject(JSON.toJSONString(param)), type);
    }

    /**
     * 发送请求
     */
    public <T> YstResponse<T> sendRequest(String api, String transCode, JSONObject param, Class<T> type)
    {
        try {
            YstRequest request = assembleRequest(transCode, param.toString());
            log.info("request:{}", request);
            String respStr = post(request.toString(), config.getHost() + api);
            log.info("response:{}", respStr);
            verify(respStr);
            log.info("验签成功");
            YstResponse<T> resp = JSON.parseObject(respStr, new TypeReference<YstResponse<T>>() {});
            ServiceUtil.assertion(!resp.isSuccess(), resp.getMsg());
            String bizDataStr = resp.getBizData() != null ? resp.getBizData().toString() : null;
            if (StringUtils.isNotBlank(bizDataStr)) {
                resp.setBizData(JSON.parseObject(bizDataStr, type));
            }
            return resp;
        } catch (Exception e) {
            throw new ServiceException("【云商通】请求失败" + e.getMessage());
        }
    }

    private void verify(String respStr) throws Exception
    {
        JSONObject map = JSON.parseObject(respStr);
        String sign = (String) map.remove("sign");
        String signType = (String) map.remove("signType");
        String srcSignMsg = SM2Utils.jsonMapToStr(map);
        if (!SM2Utils.verify(this.tlPublicKey, srcSignMsg, sign))
        {
            throw new Exception("响应报文验签失败");
        }
    }

    private <T> YstRequest assembleRequest(String transCode, String param) throws Exception
    {
        YstRequest request = new YstRequest();
        request.setAppId(this.config.getAppId());
        request.setSpAppId(this.config.getSpAppId());
        request.setTransCode(transCode);
        request.setFormat(this.config.getFormat());
        request.setCharset(this.config.getCharset());
        request.setTransDate(new SimpleDateFormat(YYYY_MM_DD).format(new Date()));
        request.setTransTime(new SimpleDateFormat(HH_MM_SS).format(new Date()));
        request.setVersion(this.config.getVersion());
        request.setBizData(param);
        String signedValue = SM2Utils.jsonMapToStr((JSONObject) JSON.toJSON(request));
        log.info("待签名源串:{}", signedValue);
        String sign = SM2Utils.sign(this.privateKey, signedValue);
        request.setSignType(this.config.getSignType());
        request.setSign(sign);
        return request;
    }

    /**
     * 发送请求
     *
     * @param param
     * @return
     * @throws IOException
     */
    private String post(String param, String url) throws IOException
    {
        if (url == null) {
            throw new RuntimeException("url不允许为空");
        }
        StringBuilder result = new StringBuilder();
        BufferedWriter writer = null;
        BufferedReader reader = null;

        try
        {
            URL httpUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) httpUrl.openConnection();
            if (connection instanceof HttpsURLConnection)
            {
                ((HttpsURLConnection) connection).setSSLSocketFactory(TxTrustManager.instance().getSSLSocketFactory());
            }
            connection.setRequestProperty("Connection", "keep-alive");
            connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setDoInput(true);
            writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8));
            writer.write(param);
            writer.flush();
            // 响应
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
            while (true)
            {
                String line = reader.readLine();
                if (line == null)
                {
                    break;
                }
                result.append(line);
            }
        }
        catch (Exception e2)
        {
            this.log.error(e2.getMessage(), e2);
        }
        finally
        {
            if (writer != null)
            {
                try
                {
                    writer.close();
                }
                catch (IOException e)
                {
                    this.log.error(e.getMessage(), e);
                }
            }
            if (reader != null)
            {
                reader.close();
            }
        }
        return result.toString();
    }

}
