using Microsoft.Extensions.Logging;
using ZB.MOM.WW.LmxProxy.Client;
using ZB.MOM.WW.LmxProxy.Client.Domain;

namespace ScadaLink.DataConnectionLayer.Adapters;

/// <summary>
/// Production ILmxProxyClient that delegates to the real
/// <see cref="ZB.MOM.WW.LmxProxy.Client.LmxProxyClient"/> library.
/// </summary>
internal class RealLmxProxyClient : ILmxProxyClient
{
    private readonly ZB.MOM.WW.LmxProxy.Client.LmxProxyClient _inner;

    public RealLmxProxyClient(ZB.MOM.WW.LmxProxy.Client.LmxProxyClient inner)
    {
        _inner = inner;
    }

    public bool IsConnected => _inner.IsConnected;

    public Task ConnectAsync(CancellationToken cancellationToken = default)
        => _inner.ConnectAsync(cancellationToken);

    public Task DisconnectAsync()
        => _inner.DisconnectAsync();

    public Task<Vtq> ReadAsync(string address, CancellationToken cancellationToken = default)
        => _inner.ReadAsync(address, cancellationToken);

    public Task<IDictionary<string, Vtq>> ReadBatchAsync(IEnumerable<string> addresses, CancellationToken cancellationToken = default)
        => _inner.ReadBatchAsync(addresses, cancellationToken);

    public Task WriteAsync(string address, TypedValue value, CancellationToken cancellationToken = default)
        => _inner.WriteAsync(address, value, cancellationToken);

    public Task WriteBatchAsync(IDictionary<string, TypedValue> values, CancellationToken cancellationToken = default)
        => _inner.WriteBatchAsync(values, cancellationToken);

    public async Task<ILmxSubscription> SubscribeAsync(
        IEnumerable<string> addresses,
        Action<string, Vtq> onUpdate,
        Action<Exception>? onStreamError = null,
        CancellationToken cancellationToken = default)
    {
        var innerSub = await _inner.SubscribeAsync(addresses, onUpdate, onStreamError, cancellationToken);
        return new SubscriptionWrapper(innerSub);
    }

    public async ValueTask DisposeAsync()
    {
        await _inner.DisposeAsync();
    }

    private sealed class SubscriptionWrapper(ZB.MOM.WW.LmxProxy.Client.LmxProxyClient.ISubscription inner) : ILmxSubscription
    {
        public async ValueTask DisposeAsync()
        {
            await inner.DisposeAsync();
        }
    }
}

/// <summary>
/// Production factory that creates LmxProxy clients using the real library's builder.
/// </summary>
public class RealLmxProxyClientFactory : ILmxProxyClientFactory
{
    private readonly ILoggerFactory _loggerFactory;

    public RealLmxProxyClientFactory(ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;
    }

    public ILmxProxyClient Create(string host, int port, string? apiKey, bool useTls = false)
    {
        var builder = new LmxProxyClientBuilder()
            .WithHost(host)
            .WithPort(port)
            .WithLogger(_loggerFactory.CreateLogger<ZB.MOM.WW.LmxProxy.Client.LmxProxyClient>());

        if (!string.IsNullOrEmpty(apiKey))
            builder.WithApiKey(apiKey);

        if (useTls)
            builder.WithSslCredentials(null);

        var client = builder.Build();
        return new RealLmxProxyClient(client);
    }
}
