package com.chaozhanggui.system.lkl.config;

import com.alipay.api.internal.util.file.IOUtils;
import com.chaozhanggui.common.system.config.MsgException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;


@Configuration
@ConfigurationProperties(prefix = "lkl")
@Data
@Slf4j
public class LakalaConfig {


    /**
     * API schema ,固定 LKLAPI-SHA256withRSA
     */
    public final static String SCHEMA = "LKLAPI-SHA256withRSA";

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final SecureRandom RANDOM = new SecureRandom();

    private String privateKey;
    private String appletsAppid;
    private String appid;
    private String mchSerialNo;
    private String merchantNo;
    private String vposId;
    private String termNo;
    private String apiUrl;
    private String privateKeyPath;
    private String certificatePath;
    private String returncatePath;
    private String callBackUrl;
    private String pubKey;



    public String request(String body,String mothod) {
        String encode = encode("client_id:client_secret".getBytes());
        System.out.println("base64:" + encode);

        try {

            String authorization = getAuthorization(body, this.getAppid(), this.getMchSerialNo(), this.getPrivateKeyPath());
            HttpResponse lakalaResponse = post(this.apiUrl + mothod, body, authorization, "utf-8");
            if (lakalaResponse.getStatusLine().getStatusCode() != 200) {
                log.error("请求失败,statusCode:{},message:{}", lakalaResponse.getStatusLine(), IOUtils.toString(lakalaResponse.getEntity().getContent(), "utf-8"));
                MsgException.throwException(IOUtils.toString(lakalaResponse.getEntity().getContent(), "utf-8"));
            }

            String appid = getHeadValue(lakalaResponse, "Lklapi-Appid");
            String lklapiSerial = getHeadValue(lakalaResponse, "Lklapi-Serial");
            String timestamp = getHeadValue(lakalaResponse, "Lklapi-Timestamp");
            String nonce = getHeadValue(lakalaResponse, "Lklapi-Nonce");
            String signature = getHeadValue(lakalaResponse, "Lklapi-Signature");
            String responseStr = IOUtils.toString(lakalaResponse.getEntity().getContent(), "utf-8");
            log.info("返回的信息为 responseStr:{}", responseStr);

            String source = appid + "\n" + lklapiSerial + "\n" + timestamp + "\n" + nonce + "\n" + responseStr + "\n";
            log.info("请求的数据为:{}", source);
            X509Certificate lklCertificate = loadCertificate(new FileInputStream(new File(this.getCertificatePath())));
            boolean verify = verify(lklCertificate, source.getBytes("utf-8"), signature);

            if (verify) {
                log.info("验签通过");
                return responseStr;
            } else {
                return null;
            }

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }


    private static String encode(byte[] content) {
        return new sun.misc.BASE64Encoder().encode(content);
    }

    private static String getHeadValue(HttpResponse response, String key) {
        return (response.containsHeader(key)) ? response.getFirstHeader(key).getValue() : "";
    }


    private static X509Certificate loadCertificate(InputStream inputStream) {
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);

            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书", e);
        }
    }

    private static boolean verify(X509Certificate certificate, byte[] message, String signature) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initVerify(certificate);
            sign.update(message);
            byte[] signatureB = Base64.decodeBase64(signature);
            return sign.verify(signatureB);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
        } catch (SignatureException e) {
            throw new RuntimeException("签名验证过程发生了错误", e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("无效的证书", e);
        }
    }



    private static HttpResponse post(String url, String message, String authorization, String ENCODING) throws Exception {
        SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] xcs, String str) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] xcs, String str) {
            }
        };
        HttpClient http = new DefaultHttpClient();
        ClientConnectionManager ccm = http.getConnectionManager();
        ctx.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory ssf = new SSLSocketFactory(ctx);
        ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        SchemeRegistry registry = ccm.getSchemeRegistry();
        registry.register(new Scheme("https", ssf, 443));
        HttpPost post = new HttpPost(url);
        StringEntity myEntity = new StringEntity(message, ENCODING);
        post.setEntity(myEntity);
        post.setHeader("Authorization", SCHEMA + " " + authorization);
        post.setHeader("Accept", "application/json");
        post.setHeader("Content-Type", "application/json");
        return http.execute(post);
    }



    private static final String getAuthorization(String body, String appid, String mchSerialNo, String merchantPrivateKeyPath) throws IOException {
        String nonceStr = generateNonceStr();
        long timestamp = generateTimestamp();

        String message = appid + "\n" + mchSerialNo + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";

        System.out.println("getToken message :  " + message);

        PrivateKey merchantPrivateKey = loadPrivateKey(new FileInputStream(new File(merchantPrivateKeyPath)));

        String signature = sign(message.getBytes("utf-8"), merchantPrivateKey);

        String authorization = "appid=\"" + appid + "\"," + "serial_no=\"" + mchSerialNo + "\"," + "timestamp=\""
                + timestamp + "\"," + "nonce_str=\"" + nonceStr + "\"," + "signature=\"" + signature + "\"";
        System.out.println("authorization message :" + authorization);

        return authorization;
    }


    private static long generateTimestamp() {
        return System.currentTimeMillis() / 1000;
    }

    private  static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }


    private static PrivateKey loadPrivateKey(InputStream inputStream) {
        try {
            ByteArrayOutputStream array = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                array.write(buffer, 0, length);
            }

            String privateKey = array.toString("utf-8").replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        } catch (IOException e) {
            throw new RuntimeException("无效的密钥");
        }
    }


    private static String sign(byte[] message, PrivateKey privateKey) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey);
            sign.update(message);
            return new String(Base64.encodeBase64(sign.sign()));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
        } catch (SignatureException e) {
            throw new RuntimeException("签名计算失败", e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("无效的私钥", e);
        }
    }


}
