package com.sunyard.chsm.sdf;

import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
import com.googlecode.jsonrpc4j.ProxyUtil;
import com.sunyard.chsm.sdf.adapter.RpcSdfAdapter;
import com.sunyard.chsm.sdf.adapter.SdfApiAdapter;
import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory;
import com.sunyard.chsm.sdf.context.AlgId;
import com.sunyard.chsm.sdf.model.DeviceInfo;
import com.sunyard.chsm.sdf.model.EccCipher;
import com.sunyard.chsm.sdf.model.EccKey;
import com.sunyard.chsm.sdf.model.EccPriKey;
import com.sunyard.chsm.sdf.model.EccPubKey;
import com.sunyard.chsm.utils.CodecUtils;
import com.sunyard.chsm.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.net.URL;
import java.util.Collections;
import java.util.Optional;

/**
 * @author liulu
 * @since 2024/12/12
 */
@Slf4j
class SdfApiTest {

    private final byte[] symKey = "nhkdhaksd4678787".getBytes();
    private final byte[] iv = "hjdashde83252i23".getBytes();
    private final String plain = "hello sdf api ,hello sdf api !&!";
    private final String ip1 = "172.16.18.41";
    private final int port = 8889;

    private RpcSdfAdapter rpcSdfAdapter;
    private String hd;
    private String hs;
    private EccPubKey pubKey;
    private EccPriKey priKey;
    private SdfApiAdapter bcAdapter = SdfApiAdapterFactory.getBcAdapter();

    @BeforeEach
    public void before() throws Exception {
        JsonRpcHttpClient client = new JsonRpcHttpClient(JsonUtils.objectMapper(),
                new URL("http://172.16.18.46:9989/sdf/adapter"), Collections.emptyMap());
        rpcSdfAdapter = ProxyUtil.createClientProxy(
                getClass().getClassLoader(), RpcSdfAdapter.class, client);
        this.hd = rpcSdfAdapter.openDevice(ip1, port, 3000, 3000, 0);
        this.hs = rpcSdfAdapter.openSession(hd);
        EccKey eccKey = rpcSdfAdapter.generateKeyPairECC(hs, AlgId.SGD_SM2_3);
        pubKey = EccPubKey.fromBytes(eccKey.getPubKey());
        priKey = EccPriKey.fromBytes(eccKey.getPriKey());
    }

    @AfterEach
    public void after() {
        Optional.ofNullable(hs).ifPresent(rpcSdfAdapter::closeSession);
        Optional.ofNullable(hd).ifPresent(rpcSdfAdapter::closeDevice);
    }

    @Test
    public void getDeviceInfo() {
        DeviceInfo deviceInfo = rpcSdfAdapter.getDeviceInfo(hs);
        Assertions.assertNotNull(deviceInfo);
        log.info("DeviceInfo: {}", deviceInfo);
    }

    @Test
    public void testSymEncAndDec() {
        String hk = rpcSdfAdapter.importKey(hs, symKey);
        byte[] ecbCipher = rpcSdfAdapter.symEncrypt(hs, hk, AlgId.SGD_SM4_ECB, null, plain.getBytes());
        byte[] ecbPlain = rpcSdfAdapter.symDecrypt(hs, hk, AlgId.SGD_SM4_ECB, null, ecbCipher);
        log.info("ecb_cipher: {}", CodecUtils.encodeHex(ecbCipher));
        Assertions.assertEquals(plain, new String(ecbPlain));

        byte[] cbcCipher = rpcSdfAdapter.symEncrypt(hs, hk, AlgId.SGD_SM4_CBC, iv, plain.getBytes());
        log.info("cbc_cipher: {}", CodecUtils.encodeHex(cbcCipher));
        byte[] cbcPlain = rpcSdfAdapter.symDecrypt(hs, hk, AlgId.SGD_SM4_CBC, iv, cbcCipher);
        Assertions.assertEquals(plain, new String(cbcPlain));
        rpcSdfAdapter.destroyKey(hs, hk);

        Assertions.assertArrayEquals(ecbPlain, cbcPlain);
        Assertions.assertNotEquals(CodecUtils.encodeHex(ecbCipher), CodecUtils.encodeHex(cbcCipher));


        String bchk = bcAdapter.importKey("", symKey);
        byte[] bcEcbCipher = bcAdapter.symEncrypt("", bchk, AlgId.SGD_SM4_ECB, null, plain.getBytes());
        log.info("bc_ecb_cipher: {}", CodecUtils.encodeHex(bcEcbCipher));
        Assertions.assertArrayEquals(ecbCipher, bcEcbCipher);

        byte[] bcCbcCipher = bcAdapter.symEncrypt("", bchk, AlgId.SGD_SM4_CBC, iv, plain.getBytes());
        log.info("bc_cbc_cipher: {}", CodecUtils.encodeHex(bcCbcCipher));
        bcAdapter.destroyKey(hs, bchk);
        Assertions.assertArrayEquals(cbcCipher, bcCbcCipher);
    }

    @Test
    public void testSM2EncAndDec() {
        EccCipher sdfCipher = rpcSdfAdapter.externalEncryptECC(hs, pubKey, plain.getBytes());
        log.info("sdf sm2 cipher: {}", sdfCipher.getC1C3C2Hex());
        byte[] bcPlain = bcAdapter.externalDecryptECC("", priKey, sdfCipher);
        log.info("bc sm2 plain: {}", new String(bcPlain));
        Assertions.assertEquals(plain, new String(bcPlain));

        EccCipher bcCipher = bcAdapter.externalEncryptECC("", pubKey, plain.getBytes());
        log.info("bc sm2 cipher: {}", bcCipher.getC1C3C2Hex());
        byte[] sm2Plain = rpcSdfAdapter.externalDecryptECC(hs, priKey, bcCipher);
        log.info("sdf sm2 plain: {}", new String(sm2Plain));

        Assertions.assertEquals(plain, new String(sm2Plain));
    }

    @Test
    public void testHash() {
        String newSession = rpcSdfAdapter.openSession(hd);
        rpcSdfAdapter.hashInit(newSession, AlgId.SGD_SM3, null, new byte[0]);
        rpcSdfAdapter.hashUpdate(newSession, plain.getBytes());
        byte[] hash = rpcSdfAdapter.hashFinish(newSession);
        log.info("sdf hash: {}", CodecUtils.encodeHex(hash));
        rpcSdfAdapter.closeSession(newSession);

        String bcNewSession = bcAdapter.openSession("");
        bcAdapter.hashInit(bcNewSession, AlgId.SGD_SM3, null, new byte[0]);
        bcAdapter.hashUpdate(bcNewSession, plain.getBytes());
        byte[] bcHash = bcAdapter.hashFinish(bcNewSession);
        log.info("bc hash: {}", CodecUtils.encodeHex(bcHash));
        bcAdapter.closeSession(bcNewSession);

        Assertions.assertArrayEquals(hash, bcHash);
    }

    @Test
    public void testSm4Mac() {
        String hk = rpcSdfAdapter.importKey(hs, symKey);
        byte[] sdfMac = rpcSdfAdapter.calculateMAC(hs, hk, AlgId.SGD_SM4_MAC, iv, plain.getBytes());
        log.info("sdf mac: {}", CodecUtils.encodeHex(sdfMac));
        rpcSdfAdapter.destroyKey(hs, hk);

        String bchk = bcAdapter.importKey("", symKey);
        byte[] bcMac = bcAdapter.calculateMAC("", bchk, AlgId.SGD_SM4_MAC, iv, plain.getBytes());
        log.info("bc mac: {}", CodecUtils.encodeHex(bcMac));
        bcAdapter.destroyKey("", bchk);
        Assertions.assertArrayEquals(sdfMac, bcMac);
    }


}
