package org.apache.seatunnel.datasource.plugin.kepserver;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.stack.core.Stack;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.Security;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;

public class KepServerClientRunner {

    static {
        // Required for SecurityPolicy.Aes256_Sha256_RsaPss
        Security.addProvider(new BouncyCastleProvider());
    }

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final CompletableFuture<OpcUaClient> future = new CompletableFuture<>();

    private final KepServerClient kepServerClient;

    public KepServerClientRunner(KepServerClient kepServerClient) throws Exception {
        this(kepServerClient, true);
    }

    public KepServerClientRunner(KepServerClient kepServerClient, boolean serverRequired) throws Exception {
        this.kepServerClient = kepServerClient;
    }

    private OpcUaClient createClient() throws Exception {

        // 加载证书
//        String filePath = KepServerClientRunner.class.getResource("/").getPath();
//        File file = new File(filePath);
//        KeyStoreLoader loader = new KeyStoreLoader().load(file.toPath());

        return OpcUaClient.create(
            kepServerClient.getEndpointUrl(),
            endpoints ->
                endpoints.stream()
                    .filter(kepServerClient.endpointFilter())
                    .findFirst(),
            configBuilder ->
                configBuilder
                    .setApplicationName(LocalizedText.english("eclipse milo opc-ua client"))
                    .setApplicationUri("urn:eclipse:milo:examples:client")
//                    .setKeyPair(loader.getClientKeyPair()) // 密钥对
//                    .setCertificate(loader.getClientCertificate()) // 证书
//                    .setCertificateChain(loader.getClientCertificateChain()) // 证书信任链
//                    .setCertificateValidator(certificateValidator) // 验证证书
                    .setIdentityProvider(kepServerClient.getIdentityProvider())
                    .setRequestTimeout(uint(5000))
                    .build()
        );
    }

    public Map run() {
        Map map=new HashMap<>();
        try {
            OpcUaClient client = createClient();

            future.whenCompleteAsync((c, ex) -> {
                if (ex != null) {
                    logger.error("Error running example: {}", ex.getMessage(), ex);
                }

                try {
                    client.disconnect().get();
                    Stack.releaseSharedResources();
                } catch (InterruptedException | ExecutionException e) {
                    logger.error("Error disconnecting: {}", e.getMessage(), e);
                }

                try {
                    Thread.sleep(1000);
                    System.exit(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });

            try {
                map = kepServerClient.run(client, future);
                future.get(15, TimeUnit.SECONDS);
            } catch (Throwable t) {
                logger.error("Error running client example: {}", t.getMessage(), t);
                future.completeExceptionally(t);
            }
        } catch (Throwable t) {
            logger.error("Error getting client: {}", t.getMessage(), t);

            future.completeExceptionally(t);

            try {
                Thread.sleep(1000);
                System.exit(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            Thread.sleep(999_999_999);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return map;
    }

}
