package com.sunyard.chsm.pool;

import com.sunyard.chsm.sdf.adapter.SdfApiAdapter;
import com.sunyard.chsm.sdf.context.AlgId;
import com.sunyard.chsm.sdf.model.EccCipher;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.DestroyMode;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Arrays;

/**
 * @author liulu
 * @since 2024/12/10
 */
@Slf4j
public class TMKContextFactory extends BasePooledObjectFactory<TMKContext> {

    private static final byte[] checkData = "1234567812345678".getBytes();

    private final SdfApiAdapter sdfApiAdapter;
    private final DeviceContext context;
    private String deviceHandle;

    public TMKContextFactory(SdfApiAdapter sdfApiAdapter, DeviceContext context) {
        this.sdfApiAdapter = sdfApiAdapter;
        this.context = context;
        this.deviceHandle = sdfApiAdapter.openDevice();
    }


    @Override
    public TMKContext create() throws Exception {
        TMKContext keyHandle = new TMKContext();
        String hs;
        try {
            hs = sdfApiAdapter.openSession(deviceHandle);
        } catch (Exception ex) {
            // 再次尝试,
            this.deviceHandle = sdfApiAdapter.openDevice();
            hs = sdfApiAdapter.openSession(deviceHandle);
        }
        keyHandle.setDeviceHandle(deviceHandle);
        keyHandle.setSessionHandle(hs);
        keyHandle.setEncTmk(context.getEncTmk());

        sdfApiAdapter.getPrivateKeyAccessRight(hs, context.getEncKeyIdx(), context.getAccessCredentials().getBytes());
        String hk = sdfApiAdapter.importKeyWithISKECC(hs, context.getEncKeyIdx(), EccCipher.fromHex(context.getEncTmk()));
        keyHandle.setKeyHandle(hk);
        keyHandle.setSdfApiAdapter(sdfApiAdapter);
        log.info("create key handle with isk for {}", context.getIpPort());
        return keyHandle;
    }

    @Override
    public PooledObject<TMKContext> wrap(TMKContext tmkContext) {
        return new DefaultPooledObject<>(tmkContext);
    }


    @Override
    public boolean validateObject(PooledObject<TMKContext> p) {
        log.info("开始检查设备{}, session 是否有效", context.getIpPort());
        TMKContext handle = p.getObject();
        if (handle == null) {
            return false;
        }
        try {
            byte[] encData = sdfApiAdapter.symEncrypt(handle.getSessionHandle(),
                    handle.getKeyHandle(), AlgId.SGD_SM4_ECB, new byte[0], checkData);
            byte[] decData = sdfApiAdapter.symDecrypt(handle.getSessionHandle(),
                    handle.getKeyHandle(), AlgId.SGD_SM4_ECB, new byte[0], encData);
            Assert.isTrue(Arrays.equals(checkData, decData), "密码机加解密异常");
            return true;
        } catch (Exception e) {
            log.warn("设备{}, validateObject error", context.getIpPort());
            return false;
        }
    }

    @Override
    public void destroyObject(PooledObject<TMKContext> p, DestroyMode destroyMode) throws Exception {
        TMKContext handle = p.getObject();
        if (handle == null) {
            return;
        }
        try {
            String sessionHandle = handle.getSessionHandle();
            if (StringUtils.hasText(handle.getKeyHandle())) {
                sdfApiAdapter.destroyKey(sessionHandle, handle.getKeyHandle());
            }
            sdfApiAdapter.closeSession(sessionHandle);
        } catch (Exception ex) {
            //
            log.warn("device: {}, {} destroyObject error ", context.getIpPort(), handle.getSessionHandle(), ex);
        }
    }

}
