using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using ArchestrA.MxAccess;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Galaxy;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.MxAccess;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Sta;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests;

[Trait("Category", "Unit")]
public sealed class HostStatusPushTests
{
    /// <summary>
    ///     PR 8 — when MxAccessClient.ConnectionStateChanged fires false→true→false,
    ///     MxAccessGalaxyBackend raises OnHostStatusChanged once per transition with
    ///     HostName=ClientName, RuntimeStatus="Running"/"Stopped", and a timestamp.
    ///     This is the gateway-level signal; per-platform ScanState probes are deferred.
    /// </summary>
    [Fact]
    public async Task ConnectionStateChanged_raises_OnHostStatusChanged_with_gateway_name()
    {
        using var pump = new StaPump("Test.Sta");
        await pump.WaitForStartedAsync();
        var proxy = new FakeProxy();
        var mx = new MxAccessClient(pump, proxy, "GatewayClient", new MxAccessClientOptions { AutoReconnect = false });
        using var backend = new MxAccessGalaxyBackend(
            new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
            mx,
            historian: null);

        var notifications = new ConcurrentQueue<HostConnectivityStatus>();
        backend.OnHostStatusChanged += (_, s) => notifications.Enqueue(s);

        await mx.ConnectAsync();
        await mx.DisconnectAsync();

        notifications.Count.ShouldBe(2);
        notifications.TryDequeue(out var first).ShouldBeTrue();
        first!.HostName.ShouldBe("GatewayClient");
        first.RuntimeStatus.ShouldBe("Running");
        first.LastObservedUtcUnixMs.ShouldBeGreaterThan(0);

        notifications.TryDequeue(out var second).ShouldBeTrue();
        second!.HostName.ShouldBe("GatewayClient");
        second.RuntimeStatus.ShouldBe("Stopped");
    }

    [Fact]
    public async Task Dispose_unsubscribes_so_post_dispose_state_changes_do_not_fire_events()
    {
        using var pump = new StaPump("Test.Sta");
        await pump.WaitForStartedAsync();
        var proxy = new FakeProxy();
        var mx = new MxAccessClient(pump, proxy, "GatewayClient", new MxAccessClientOptions { AutoReconnect = false });
        var backend = new MxAccessGalaxyBackend(
            new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
            mx,
            historian: null);

        var count = 0;
        backend.OnHostStatusChanged += (_, _) => Interlocked.Increment(ref count);

        await mx.ConnectAsync();
        count.ShouldBe(1);

        backend.Dispose();
        await mx.DisconnectAsync();

        count.ShouldBe(1); // no second notification after Dispose
    }

    private sealed class FakeProxy : IMxProxy
    {
        private int _next = 1;
        public int Register(string _) => 42;
        public void Unregister(int _) { }
        public int AddItem(int _, string __) => Interlocked.Increment(ref _next);
        public void RemoveItem(int _, int __) { }
        public void AdviseSupervisory(int _, int __) { }
        public void UnAdviseSupervisory(int _, int __) { }
        public void Write(int _, int __, object ___, int ____) { }
        public event MxDataChangeHandler? OnDataChange { add { } remove { } }
        public event MxWriteCompleteHandler? OnWriteComplete { add { } remove { } }
    }
}
