using ZB.MOM.WW.LmxProxy.Client.Domain;

namespace ZB.MOM.WW.LmxProxy.Client.Tests.Fakes;

/// <summary>
/// Hand-written fake implementation of ILmxProxyClient for unit testing streaming extensions.
/// </summary>
internal class FakeLmxProxyClient : ILmxProxyClient
{
    public TimeSpan DefaultTimeout { get; set; } = TimeSpan.FromSeconds(30);

    // Track calls
    public List<List<string>> ReadBatchCalls { get; } = [];
    public List<IDictionary<string, TypedValue>> WriteBatchCalls { get; } = [];
    public List<IEnumerable<string>> SubscribeCalls { get; } = [];

    // Configurable responses
    public Func<IEnumerable<string>, CancellationToken, Task<IDictionary<string, Vtq>>>? ReadBatchHandler { get; set; }
    public Exception? ReadBatchExceptionToThrow { get; set; }
    public int ReadBatchExceptionCount { get; set; }
    private int _readBatchCallCount;

    // Subscription support
    public Action<string, Vtq>? CapturedOnUpdate { get; private set; }
    public Action<Exception>? CapturedOnError { get; private set; }

    public Task ConnectAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
    public Task DisconnectAsync() => Task.CompletedTask;
    public Task<bool> IsConnectedAsync() => Task.FromResult(true);

    public Task<Vtq> ReadAsync(string address, CancellationToken cancellationToken = default)
        => Task.FromResult(new Vtq(null, DateTime.UtcNow, Quality.Good));

    public Task<IDictionary<string, Vtq>> ReadBatchAsync(IEnumerable<string> addresses, CancellationToken cancellationToken = default)
    {
        var addressList = addresses.ToList();
        ReadBatchCalls.Add(addressList);
        _readBatchCallCount++;

        if (ReadBatchExceptionToThrow is not null && _readBatchCallCount <= ReadBatchExceptionCount)
            throw ReadBatchExceptionToThrow;

        if (ReadBatchHandler is not null)
            return ReadBatchHandler(addressList, cancellationToken);

        var result = new Dictionary<string, Vtq>();
        foreach (var addr in addressList)
            result[addr] = new Vtq(42.0, DateTime.UtcNow, Quality.Good);
        return Task.FromResult<IDictionary<string, Vtq>>(result);
    }

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

    public Task WriteBatchAsync(IDictionary<string, TypedValue> values, CancellationToken cancellationToken = default)
    {
        WriteBatchCalls.Add(new Dictionary<string, TypedValue>(values));
        return Task.CompletedTask;
    }

    public Task<WriteBatchAndWaitResponse> WriteBatchAndWaitAsync(
        IDictionary<string, TypedValue> values, string flagTag, TypedValue flagValue,
        int timeoutMs = 5000, int pollIntervalMs = 100, CancellationToken cancellationToken = default)
        => Task.FromResult(new WriteBatchAndWaitResponse { Success = true });

    public Task<LmxProxyClient.ISubscription> SubscribeAsync(
        IEnumerable<string> addresses,
        Action<string, Vtq> onUpdate,
        Action<Exception>? onStreamError = null,
        CancellationToken cancellationToken = default)
    {
        SubscribeCalls.Add(addresses);
        CapturedOnUpdate = onUpdate;
        CapturedOnError = onStreamError;
        return Task.FromResult<LmxProxyClient.ISubscription>(new FakeSubscription());
    }

    public Task<LmxProxyClient.ApiKeyInfo> CheckApiKeyAsync(string apiKey, CancellationToken cancellationToken = default)
        => Task.FromResult(new LmxProxyClient.ApiKeyInfo { IsValid = true });

    public Dictionary<string, object> GetMetrics() => [];

    public void Dispose() { }
    public ValueTask DisposeAsync() => ValueTask.CompletedTask;

    private class FakeSubscription : LmxProxyClient.ISubscription
    {
        public void Dispose() { }
        public Task DisposeAsync() => Task.CompletedTask;
    }
}
