using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Hosting;

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

[Trait("Category", "Unit")]
public sealed class DriverHostTests
{
    private sealed class StubDriver(string id, bool failInit = false) : IDriver
    {
        public string DriverInstanceId { get; } = id;
        public string DriverType => "Stub";
        public bool Initialized { get; private set; }
        public bool ShutDown { get; private set; }

        public Task InitializeAsync(string _, CancellationToken ct)
        {
            if (failInit) throw new InvalidOperationException("boom");
            Initialized = true;
            return Task.CompletedTask;
        }

        public Task ReinitializeAsync(string _, CancellationToken ct) => Task.CompletedTask;
        public Task ShutdownAsync(CancellationToken ct) { ShutDown = true; return Task.CompletedTask; }
        public DriverHealth GetHealth() =>
            new(Initialized ? DriverState.Healthy : DriverState.Unknown, null, null);
        public long GetMemoryFootprint() => 0;
        public Task FlushOptionalCachesAsync(CancellationToken ct) => Task.CompletedTask;
    }

    [Fact]
    public async Task Register_initializes_driver_and_tracks_health()
    {
        await using var host = new DriverHost();
        var driver = new StubDriver("d-1");

        await host.RegisterAsync(driver, "{}", CancellationToken.None);

        host.RegisteredDriverIds.ShouldContain("d-1");
        driver.Initialized.ShouldBeTrue();
        host.GetHealth("d-1")!.State.ShouldBe(DriverState.Healthy);
    }

    [Fact]
    public async Task Register_rethrows_init_failure_but_keeps_driver_registered()
    {
        await using var host = new DriverHost();
        var driver = new StubDriver("d-bad", failInit: true);

        await Should.ThrowAsync<InvalidOperationException>(() =>
            host.RegisterAsync(driver, "{}", CancellationToken.None));

        host.RegisteredDriverIds.ShouldContain("d-bad");
    }

    [Fact]
    public async Task Duplicate_registration_throws()
    {
        await using var host = new DriverHost();
        await host.RegisterAsync(new StubDriver("d-1"), "{}", CancellationToken.None);

        await Should.ThrowAsync<InvalidOperationException>(() =>
            host.RegisterAsync(new StubDriver("d-1"), "{}", CancellationToken.None));
    }

    [Fact]
    public async Task Unregister_shuts_down_and_removes()
    {
        await using var host = new DriverHost();
        var driver = new StubDriver("d-1");
        await host.RegisterAsync(driver, "{}", CancellationToken.None);

        await host.UnregisterAsync("d-1", CancellationToken.None);

        host.RegisteredDriverIds.ShouldNotContain("d-1");
        driver.ShutDown.ShouldBeTrue();
    }
}
