using Opc.Ua;
using Opc.Ua.Configuration;
using Serilog;
using ZB.MOM.WW.OtOpcUa.Client.Shared.Models;

namespace ZB.MOM.WW.OtOpcUa.Client.Shared.Adapters;

/// <summary>
///     Production implementation that builds a real OPC UA ApplicationConfiguration.
/// </summary>
internal sealed class DefaultApplicationConfigurationFactory : IApplicationConfigurationFactory
{
    private static readonly ILogger Logger = Log.ForContext<DefaultApplicationConfigurationFactory>();

    /// <inheritdoc />
    public async Task<ApplicationConfiguration> CreateAsync(ConnectionSettings settings, CancellationToken ct)
    {
        // Resolve the canonical PKI path lazily on first use so constructing a
        // ConnectionSettings instance — including the throwaway copies the client
        // service builds per failover attempt — does not touch the filesystem.
        // Callers that supply an explicit path override the default.
        var storePath = string.IsNullOrWhiteSpace(settings.CertificateStorePath)
            ? ClientStoragePaths.GetPkiPath()
            : settings.CertificateStorePath;

        var config = new ApplicationConfiguration
        {
            ApplicationName = "OtOpcUaClient",
            ApplicationUri = "urn:localhost:OtOpcUaClient",
            ApplicationType = ApplicationType.Client,
            SecurityConfiguration = new SecurityConfiguration
            {
                ApplicationCertificate = new CertificateIdentifier
                {
                    StoreType = CertificateStoreType.Directory,
                    StorePath = Path.Combine(storePath, "own")
                },
                TrustedIssuerCertificates = new CertificateTrustList
                {
                    StoreType = CertificateStoreType.Directory,
                    StorePath = Path.Combine(storePath, "issuer")
                },
                TrustedPeerCertificates = new CertificateTrustList
                {
                    StoreType = CertificateStoreType.Directory,
                    StorePath = Path.Combine(storePath, "trusted")
                },
                RejectedCertificateStore = new CertificateTrustList
                {
                    StoreType = CertificateStoreType.Directory,
                    StorePath = Path.Combine(storePath, "rejected")
                },
                AutoAcceptUntrustedCertificates = settings.AutoAcceptCertificates
            },
            ClientConfiguration = new ClientConfiguration
            {
                DefaultSessionTimeout = settings.SessionTimeoutSeconds * 1000
            }
        };

        await config.Validate(ApplicationType.Client);

        if (settings.AutoAcceptCertificates)
            config.CertificateValidator.CertificateValidation += (_, e) => e.Accept = true;

        if (settings.SecurityMode != SecurityMode.None)
        {
            var app = new ApplicationInstance
            {
                ApplicationName = "OtOpcUaClient",
                ApplicationType = ApplicationType.Client,
                ApplicationConfiguration = config
            };
            await app.CheckApplicationInstanceCertificatesAsync(false, 2048);
        }

        Logger.Debug("ApplicationConfiguration created for {EndpointUrl}", settings.EndpointUrl);
        return config;
    }
}