using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using console_opcua_sgdn_tornio.Models;

namespace console_opcua_sgdn_tornio.Controllers
{
    public class OpcUaConnection(OpcUaSettings ous, CancellationToken ct)
    {

        private Session _session;

        public async Task ConnectAsync()
        {
            var config = new ApplicationConfiguration()
            {
                ApplicationName = ous.ApplicationName,          //"MyPalOpcUa",
                ApplicationUri = ous.ApplicationUrn,            //$"urn:{System.Net.Dns.GetHostName()}:MyPalOpcUa",
                ApplicationType = ApplicationType.Client,
                SecurityConfiguration = new SecurityConfiguration
                {
                    ApplicationCertificate = new CertificateIdentifier
                    {
                        StoreType = "Directory",
                        StorePath = "pki/own",
                        SubjectName = "MyPalOpcUa"
                    },

                    TrustedPeerCertificates = new CertificateTrustList
                    {
                        StoreType = "Directory",
                        StorePath = "pki/trusted"
                    },

                    TrustedIssuerCertificates = new CertificateTrustList
                    {
                        StoreType = "Directory",
                        StorePath = "pki/issuers"
                    },

                    RejectedCertificateStore = new CertificateTrustList
                    {
                        StoreType = "Directory",
                        StorePath = "pki/rejected"
                    },

                    RejectSHA1SignedCertificates = false,
                    MinimumCertificateKeySize = 1024,
                    AutoAcceptUntrustedCertificates = true
                },
                TransportConfigurations = new TransportConfigurationCollection(),
                TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
                ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
                TraceConfiguration = new TraceConfiguration()
            };

            await config.Validate(ApplicationType.Client);

            config.CertificateValidator.CertificateValidation += (s, e) =>
            {
                if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted ||
                    e.Error.StatusCode == StatusCodes.BadCertificateChainIncomplete)
                {
                    e.Accept = true;
                }
            };

            var application = new ApplicationInstance
            {
                ApplicationName = "MyPalOpcUa",
                ApplicationType = ApplicationType.Client,
                ApplicationConfiguration = config
            };

            await application.CheckApplicationInstanceCertificatesAsync(true, 2048, ct);

            var selectedEndpoint = await CoreClientUtils.SelectEndpointAsync(config, ous.EndpointUrl, true, discoverTimeout: 15000, ct);

            // Load user certificate and private key ! Generated from machine !
            byte[] certBytes = await File.ReadAllBytesAsync(ous.CertPath);     //File.ReadAllBytes(@"C:\Certs\TNC\opcua-user-cert.der");
            string keyPem = await File.ReadAllTextAsync(ous.KeyPath);          //File.ReadAllText(@"C:\Certs\TNC\opcua-user-cert.key.pem");

            var cert = new X509Certificate2(certBytes);
            using var rsa = RSA.Create();
            rsa.ImportFromPem(keyPem.ToCharArray());
            var certWithKey = cert.CopyWithPrivateKey(rsa);

            var epConf = new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(config));

            _session = await Session.Create(
                config,
                epConf,
                false,
                ous.ApplicationName,
                ous.SessionTimeout,
                new UserIdentity(certWithKey),
                null,
                ct
                );
        }

        public Session Session => _session;

    }
}
