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
{
    /// <summary>Verifies that Tier C hard memory breach requests supervisor recycle.</summary>
    [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");
    }

    /// <summary>Verifies that Tier A and B hard memory breach never request recycle.</summary>
    /// <param name="tier">The driver tier to test.</param>
    [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);
    }

    /// <summary>Verifies that Tier C without supervisor hard breach is a no-op.</summary>
    [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");
    }

    /// <summary>Verifies that soft memory breach never requests recycle at any tier.</summary>
    /// <param name="tier">The driver tier to test.</param>
    [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);
    }

    /// <summary>Verifies that non-breach memory actions are no-ops.</summary>
    /// <param name="action">The non-breach memory tracking action to test.</param>
    [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
    {
        /// <summary>Gets the driver instance identifier.</summary>
        public string DriverInstanceId => "fake-tier-c";
        /// <summary>Gets the count of recycle operations.</summary>
        public int RecycleCount { get; private set; }
        /// <summary>Gets the reason from the last recycle operation.</summary>
        public string? LastReason { get; private set; }

        /// <summary>Recycles the driver asynchronously.</summary>
        /// <param name="reason">The reason for recycling.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public Task RecycleAsync(string reason, CancellationToken cancellationToken)
        {
            RecycleCount++;
            LastReason = reason;
            return Task.CompletedTask;
        }
    }
}
