package com.neusoft.smart.pos.ssl.service;

import com.neusoft.smart.pos.dto.ssl.response.SSLFileResponse;
import com.neusoft.smart.pos.framework.utils.DesUtil;
import com.neusoft.smart.pos.framework.utils.RSAUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.util.Arrays;
import java.util.List;

@Service
@Slf4j
public class SSLService {
    
    @Value("${smart.pos.ssl.root.cert.path}")
    private String rootCertPath;
    
    @Value("${smart.pos.ssl.cert.folder.path}")
    private String certFolderPath;
    
    @Value("${smart.pos.ssl.root.key.path}")
    private String rootKeyPath;
    
    @Value("${smart.pos.ssl.root.key.password}")
    private String rootKeyPassword;
    
    private RSAPrivateKey privateKey;
    
    @PostConstruct
    public void init() {
        try (InputStream inputStream = new FileInputStream(new File(rootKeyPath));
             ByteArrayInputStream bais = new ByteArrayInputStream(IOUtils.toByteArray(inputStream));) {
            Security.addProvider(new BouncyCastleProvider());
            PEMParser pemParser = new PEMParser(new InputStreamReader(bais));
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
            PEMDecryptorProvider decryptionProv = new JcePEMDecryptorProviderBuilder().build(rootKeyPassword.toCharArray());
            Object keyPair = pemParser.readObject();
            PrivateKeyInfo keyInfo;
            if (keyPair instanceof PEMEncryptedKeyPair) {
                PEMKeyPair decryptedKeyPair = ((PEMEncryptedKeyPair) keyPair).decryptKeyPair(decryptionProv);
                keyInfo = decryptedKeyPair.getPrivateKeyInfo();
            } else {
                keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo();
            }
            pemParser.close();
            privateKey = (RSAPrivateKey) converter.getPrivateKey(keyInfo);
            log.error("rootCertPath:" + rootCertPath);
            log.error("certFolderPath:" + certFolderPath);
            log.error("rootKeyPath:" + rootKeyPath);
            log.error("rootKeyPassword:" + rootKeyPassword);
        } catch (Exception e) {
            log.error("PostConstruct:", e);
        }
    }
    
    /**
     * 分发证书
     *
     * @param randomStr
     * @param sn
     * @return
     */
    public SSLFileResponse issue(String randomStr, String sn) {
        randomStr = this.decrypt(randomStr);
        sn = this.decrypt(sn);
        SSLFileResponse result = new SSLFileResponse();
        Pair<File, File> pair = getCert(sn);
        try (FileInputStream fileInputStream1 = new FileInputStream(pair.getFirst());
             FileInputStream fileInputStream2 = new FileInputStream(pair.getSecond());
             FileInputStream fileInputStream3 = new FileInputStream(new File(rootCertPath))) {
            String certFileBase64 = Base64Utils.encodeToString(IOUtils.toByteArray(fileInputStream1));
            String keyFileBase64 = Base64Utils.encodeToString(IOUtils.toByteArray(fileInputStream2));
            String rootCertFileBase64 = Base64Utils.encodeToString(IOUtils.toByteArray(fileInputStream3));
            certFileBase64 = DesUtil.encrypt(certFileBase64, randomStr);
            keyFileBase64 = DesUtil.encrypt(keyFileBase64, randomStr);
            rootCertFileBase64 = DesUtil.encrypt(rootCertFileBase64, randomStr);
            result.setRecKeyCert(certFileBase64);
            result.setRecKey(keyFileBase64);
            result.setRecTrustCert(rootCertFileBase64);
        } catch (Exception e) {
            this.revert(pair);
            throw new RuntimeException(e);
        }
        if (!pair.getFirst().getName().contains("#") && !pair.getSecond().getName().contains("#")) {
            this.rename(sn, pair);
        }
        return result;
    }
    
    
    /**
     * 在文件夹中拿出来一对公钥私钥
     *
     * @return
     */
    private Pair<File, File> getCert(String sn) {
        File keyFolder = new File(certFolderPath);
        FileFilter fileFilter = f -> f.getName().startsWith(sn + "#");
        List<File> cerFiles = Arrays.asList(keyFolder.listFiles(fileFilter));
        if (!cerFiles.isEmpty()) {
            File cerFile = cerFiles.get(0).getName().endsWith("pemProcessed") ? cerFiles.get(0) : cerFiles.get(1);
            File keyFile = cerFiles.get(0).getName().endsWith("derProcessed") ? cerFiles.get(0) : cerFiles.get(1);
            return Pair.of(cerFile, keyFile);
        }
        fileFilter = f -> f.getName().endsWith(".pem");
        cerFiles = Arrays.asList(keyFolder.listFiles(fileFilter));
        if (cerFiles.isEmpty()) {
            throw new RuntimeException("证书库证书资源耗尽,请联系管理员");
        }
        File cerFile = cerFiles.get(0);
        File keyFile = new File(cerFile.getAbsolutePath().replace(".pem", ".der"));
        Pair<File, File> pair = Pair.of(cerFile, keyFile);
        if (!pair.getSecond().exists()) {
            pair = getCert(sn);
        }
        File certNewFile = new File(pair.getFirst().getAbsolutePath() + ".pemProcessing");
        File keyNewFile = new File(pair.getSecond().getAbsolutePath() + ".derProcessing");
        pair.getFirst().renameTo(certNewFile);
        pair.getSecond().renameTo(keyNewFile);
        pair = Pair.of(certNewFile, keyNewFile);
        return pair;
    }
    
    /**
     * 分发过的文件改名字格式为 sn+"#"+文件名+"xxxProcessed"
     *
     * @param sn
     * @param filePair
     */
    private void rename(String sn, Pair<File, File> filePair) {
        File certNewFile = new File(filePair.getFirst().getParent() + File.separator +
                sn + "#" + filePair.getFirst().getName().replace("pemProcessing", "pemProcessed"));
        File keyNewFile = new File(filePair.getSecond().getParent() + File.separator +
                sn + "#" + filePair.getSecond().getName().replace("derProcessing", "derProcessed"));
        filePair.getFirst().renameTo(certNewFile);
        filePair.getSecond().renameTo(keyNewFile);
    }
    
    /**
     * 分发异常的时候,文件名恢复
     *
     * @param filePair
     */
    private void revert(Pair<File, File> filePair) {
        File certNewFile = new File(filePair.getFirst().getAbsolutePath().substring(0, filePair.getFirst().getAbsolutePath().lastIndexOf(".")));
        File keyNewFile = new File(filePair.getSecond().getAbsolutePath().substring(0, filePair.getSecond().getAbsolutePath().lastIndexOf(".")));
        filePair.getFirst().renameTo(certNewFile);
        filePair.getSecond().renameTo(keyNewFile);
    }
    
    /**
     * rsa解密随机数和序列号
     *
     * @param data
     * @return
     */
    private String decrypt(String data) {
        try {
            String result = RSAUtils.decryptByPrivateKey(data, privateKey);
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
