package org.jeecg.modules.pay.core;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

/**
 * 拉卡拉支付签名工具类
 *
 * @author Mr丶xie
 * @version 1.0.0
 * @mail xielinke@lklink.cn
 */
public class LakalaPayKit {
    private static final String CHARSET_NAME = "UTF-8";

    public static String getAuthorization(String body) throws UnsupportedEncodingException {
        String nonceStr = generateNonceStr();
        long timestamp = System.currentTimeMillis() / 1000;
        String message = LakalaPayConfig.APPID + "\n" + LakalaPayConfig.SERIAL_NO + "\n" + timestamp + "\n" + nonceStr + "\n" + body  + "\n";
        String signature = sign(message.getBytes(CHARSET_NAME), loadPrivateKey());
        String authorization = "appid=\"" + LakalaPayConfig.APPID + "\"," + "serial_no=\"" + LakalaPayConfig.SERIAL_NO + "\",timestamp=\"" + timestamp + "\"," + "nonce_str=\"" + nonceStr + "\"," + "signature=\"" + signature + "\"";
        return authorization;
    }

    public static boolean verify(byte[] message, String signature) {
        try {
            X509Certificate certificate = loadCertificate();
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initVerify(certificate);
            sign.update(message);
            byte[] signatureB = Base64.getDecoder().decode(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 X509Certificate loadCertificate() {
        try (InputStream inputStream = LakalaPayKit.class.getClassLoader().getResourceAsStream(LakalaPayConfig.SECRET_PUBLIC_KEY_NAME)) {
            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);
        } catch (IOException e1) {
            throw new RuntimeException(e1);
        }
    }

    /**
     * 密钥加载
     * 目前指定加载 商户密钥证书
     **/
    private static PrivateKey loadPrivateKey() {
        try (InputStream inputStream = LakalaPayKit.class.getClassLoader().getResourceAsStream(LakalaPayConfig.SECRET_PRIVATE_KEY_NAME)) {
            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.getDecoder().decode(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.getEncoder().encode(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);
        }
    }

    /**
     * 生成随机字符串
     **/
    private static String generateNonceStr() {
        char[] nonceChars = new char[12];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = LakalaPayConfig.STR.charAt(new SecureRandom().nextInt(LakalaPayConfig.STR.length()));
        }
        return new String(nonceChars);
    }


}
