using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Drivers;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests;

/// <summary>
///     Covers the in-process push contract the Blazor Server driver status panel's resilience section
///     relies on: <see cref="IDriverResilienceStatusStore.SnapshotChanged"/> fires on every
///     <see cref="IDriverResilienceStatusStore.Upsert"/>, keyed per <c>(instance, host)</c> with
///     last-write-wins, and <c>GetForInstance</c> returns only the queried instance's hosts. Mirrors
///     <see cref="DriverStatusSnapshotStoreTests"/>; the panel reads this store directly (no
///     self-targeted SignalR connection).
/// </summary>
public sealed class DriverResilienceStatusStoreTests
{
    private static DriverResilienceStatusChanged Msg(
        string instance, string host, bool breakerOpen = false, int failures = 0) =>
        new(instance, host, breakerOpen, failures, 0, null,
            new DateTime(2026, 7, 13, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2026, 7, 13, 0, 0, 0, DateTimeKind.Utc));

    [Fact]
    public void Upsert_raises_SnapshotChanged_with_the_stored_snapshot()
    {
        var store = new InMemoryDriverResilienceStatusStore();
        var received = new List<DriverResilienceStatusChanged>();
        store.SnapshotChanged += received.Add;

        var msg = Msg("drv-1", "host-a", breakerOpen: true);
        store.Upsert(msg);

        received.Count.ShouldBe(1);
        received[0].ShouldBeSameAs(msg);
    }

    [Fact]
    public void Upsert_is_last_write_wins_per_instance_host()
    {
        var store = new InMemoryDriverResilienceStatusStore();
        store.Upsert(Msg("drv-1", "host-a", failures: 3));
        store.Upsert(Msg("drv-1", "host-a", failures: 0));

        var hosts = store.GetForInstance("drv-1");
        hosts.Count.ShouldBe(1);
        hosts[0].ConsecutiveFailures.ShouldBe(0);
    }

    [Fact]
    public void GetForInstance_returns_only_the_queried_instances_hosts()
    {
        var store = new InMemoryDriverResilienceStatusStore();
        store.Upsert(Msg("drv-1", "host-a"));
        store.Upsert(Msg("drv-1", "host-b"));
        store.Upsert(Msg("drv-2", "host-a"));

        var hosts = store.GetForInstance("drv-1");

        hosts.Count.ShouldBe(2);
        hosts.ShouldAllBe(h => h.DriverInstanceId == "drv-1");
    }

    [Fact]
    public void GetForInstance_returns_empty_for_unknown_instance()
    {
        var store = new InMemoryDriverResilienceStatusStore();
        store.Upsert(Msg("drv-1", "host-a"));

        store.GetForInstance("nope").ShouldBeEmpty();
    }

    [Fact]
    public void Unsubscribed_handler_stops_receiving_after_removal()
    {
        var store = new InMemoryDriverResilienceStatusStore();
        var count = 0;
        void Handler(DriverResilienceStatusChanged _) => count++;

        store.SnapshotChanged += Handler;
        store.Upsert(Msg("drv-1", "host-a"));
        store.SnapshotChanged -= Handler;
        store.Upsert(Msg("drv-1", "host-a"));

        count.ShouldBe(1);
    }
}
