using Microsoft.Extensions.Logging.Abstractions;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Stability;

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

[Trait("Category", "Unit")]
public sealed class MemoryRecycleTests
{
    [Fact]
    public async Task TierC_HardBreach_RequestsSupervisorRecycle()
    {
        var supervisor = new FakeSupervisor();
        var recycle = new MemoryRecycle(DriverTier.C, supervisor, NullLogger<MemoryRecycle>.Instance);

        var requested = await recycle.HandleAsync(MemoryTrackingAction.HardBreach, 2_000_000_000, CancellationToken.None);

        requested.ShouldBeTrue();
        supervisor.RecycleCount.ShouldBe(1);
        supervisor.LastReason.ShouldContain("hard-breach");
    }

    [Theory]
    [InlineData(DriverTier.A)]
    [InlineData(DriverTier.B)]
    public async Task InProcessTier_HardBreach_NeverRequestsRecycle(DriverTier tier)
    {
        var supervisor = new FakeSupervisor();
        var recycle = new MemoryRecycle(tier, supervisor, NullLogger<MemoryRecycle>.Instance);

        var requested = await recycle.HandleAsync(MemoryTrackingAction.HardBreach, 2_000_000_000, CancellationToken.None);

        requested.ShouldBeFalse("Tier A/B hard-breach logs a promotion recommendation only (decisions #74, #145)");
        supervisor.RecycleCount.ShouldBe(0);
    }

    [Fact]
    public async Task TierC_WithoutSupervisor_HardBreach_NoOp()
    {
        var recycle = new MemoryRecycle(DriverTier.C, supervisor: null, NullLogger<MemoryRecycle>.Instance);

        var requested = await recycle.HandleAsync(MemoryTrackingAction.HardBreach, 2_000_000_000, CancellationToken.None);

        requested.ShouldBeFalse("no supervisor → no recycle path; action logged only");
    }

    [Theory]
    [InlineData(DriverTier.A)]
    [InlineData(DriverTier.B)]
    [InlineData(DriverTier.C)]
    public async Task SoftBreach_NeverRequestsRecycle(DriverTier tier)
    {
        var supervisor = new FakeSupervisor();
        var recycle = new MemoryRecycle(tier, supervisor, NullLogger<MemoryRecycle>.Instance);

        var requested = await recycle.HandleAsync(MemoryTrackingAction.SoftBreach, 1_000_000_000, CancellationToken.None);

        requested.ShouldBeFalse("soft-breach is surface-only at every tier");
        supervisor.RecycleCount.ShouldBe(0);
    }

    [Theory]
    [InlineData(MemoryTrackingAction.None)]
    [InlineData(MemoryTrackingAction.Warming)]
    public async Task NonBreachActions_NoOp(MemoryTrackingAction action)
    {
        var supervisor = new FakeSupervisor();
        var recycle = new MemoryRecycle(DriverTier.C, supervisor, NullLogger<MemoryRecycle>.Instance);

        var requested = await recycle.HandleAsync(action, 100_000_000, CancellationToken.None);

        requested.ShouldBeFalse();
        supervisor.RecycleCount.ShouldBe(0);
    }

    private sealed class FakeSupervisor : IDriverSupervisor
    {
        public string DriverInstanceId => "fake-tier-c";
        public int RecycleCount { get; private set; }
        public string? LastReason { get; private set; }

        public Task RecycleAsync(string reason, CancellationToken cancellationToken)
        {
            RecycleCount++;
            LastReason = reason;
            return Task.CompletedTask;
        }
    }
}
