using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Client.Domain;
using ZB.MOM.WW.LmxProxy.Client.Tests.Fakes;

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

public class LmxProxyClientConnectionTests
{
    [Fact]
    public async Task IsConnectedAsync_ReturnsFalseBeforeConnect()
    {
        var client = new LmxProxyClient("localhost", 50051, null, null);

        var result = await client.IsConnectedAsync();

        result.Should().BeFalse();
        client.Dispose();
    }

    [Fact]
    public async Task IsConnectedAsync_ReturnsTrueAfterInjection()
    {
        var (client, _) = TestableClient.CreateConnected();

        var result = await client.IsConnectedAsync();

        result.Should().BeTrue();
        client.Dispose();
    }

    [Fact]
    public async Task DisconnectAsync_SendsDisconnectAndClearsState()
    {
        var (client, fake) = TestableClient.CreateConnected();

        await client.DisconnectAsync();

        fake.DisconnectCalls.Should().HaveCount(1);
        fake.DisconnectCalls[0].SessionId.Should().Be("test-session-123");
        client.IsConnected.Should().BeFalse();
        client.Dispose();
    }

    [Fact]
    public async Task DisconnectAsync_SwallowsExceptions()
    {
        var (client, fake) = TestableClient.CreateConnected();
        fake.DisconnectResponseToReturn = null!; // Force an error path

        // Should not throw
        var act = () => client.DisconnectAsync();
        await act.Should().NotThrowAsync();

        client.Dispose();
    }

    [Fact]
    public void IsConnected_ReturnsFalseAfterDispose()
    {
        var (client, _) = TestableClient.CreateConnected();

        client.Dispose();

        client.IsConnected.Should().BeFalse();
    }

    [Fact]
    public async Task MarkDisconnectedAsync_ClearsConnectionState()
    {
        var (client, _) = TestableClient.CreateConnected();

        await client.MarkDisconnectedAsync(new Exception("connection lost"));

        client.IsConnected.Should().BeFalse();
        client.Dispose();
    }

    [Fact]
    public void DefaultTimeout_RejectsOutOfRange()
    {
        var client = new LmxProxyClient("localhost", 50051, null, null);

        var act = () => client.DefaultTimeout = TimeSpan.FromMilliseconds(500);
        act.Should().Throw<ArgumentOutOfRangeException>();

        var act2 = () => client.DefaultTimeout = TimeSpan.FromMinutes(11);
        act2.Should().Throw<ArgumentOutOfRangeException>();

        client.Dispose();
    }

    [Fact]
    public void DefaultTimeout_AcceptsValidRange()
    {
        var client = new LmxProxyClient("localhost", 50051, null, null);

        client.DefaultTimeout = TimeSpan.FromSeconds(5);
        client.DefaultTimeout.Should().Be(TimeSpan.FromSeconds(5));

        client.Dispose();
    }
}
