using Akka.Actor;
using Akka.TestKit.Xunit2;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using ScadaLink.Commons.Interfaces.Repositories;
using ScadaLink.Communication.Actors;

namespace ScadaLink.Communication.Tests;

/// <summary>
/// Regression tests for Communication-004 — coordinator actors must declare an
/// explicit <c>Resume</c> supervision strategy per the CLAUDE.md decision
/// ("Resume for coordinator actors"). A child fault under the default
/// (Restart) strategy would wipe a child's in-memory state; the long-lived
/// coordinators own per-site ClusterClients and must not silently discard
/// their children on a transient fault.
/// </summary>
public class CoordinatorSupervisionTests : TestKit
{
    /// <summary>
    /// Test-only subclass that exposes the protected <see cref="SupervisorStrategy"/>
    /// so the configured directive can be asserted directly.
    /// </summary>
    private sealed class CentralCommunicationActorProbe : CentralCommunicationActor
    {
        public CentralCommunicationActorProbe(IServiceProvider sp, ISiteClientFactory factory)
            : base(sp, factory) { }

        public SupervisorStrategy GetSupervisorStrategy() => SupervisorStrategy();
    }

    /// <summary>
    /// Test-only subclass that exposes the protected <see cref="SupervisorStrategy"/>.
    /// </summary>
    private sealed class SiteCommunicationActorProbe : SiteCommunicationActor
    {
        public SiteCommunicationActorProbe(string siteId, CommunicationOptions options, IActorRef dm)
            : base(siteId, options, dm) { }

        public SupervisorStrategy GetSupervisorStrategy() => SupervisorStrategy();
    }

    private static IServiceProvider EmptyServiceProvider()
    {
        var mockRepo = Substitute.For<ISiteRepository>();
        mockRepo.GetAllSitesAsync(Arg.Any<CancellationToken>())
            .Returns(new List<Commons.Entities.Sites.Site>());
        var services = new ServiceCollection();
        services.AddScoped(_ => mockRepo);
        return services.BuildServiceProvider();
    }

    [Fact]
    public void CentralCommunicationActor_SupervisorStrategy_IsResume()
    {
        var sp = EmptyServiceProvider();
        var factory = Substitute.For<ISiteClientFactory>();

        var actorRef = new Akka.TestKit.TestActorRef<CentralCommunicationActorProbe>(
            Sys, Props.Create(() => new CentralCommunicationActorProbe(sp, factory)));

        var strategy = actorRef.UnderlyingActor.GetSupervisorStrategy();

        var oneForOne = Assert.IsType<OneForOneStrategy>(strategy);
        var directive = oneForOne.Decider.Decide(new InvalidOperationException("transient child fault"));
        Assert.Equal(Directive.Resume, directive);
    }

    [Fact]
    public void SiteCommunicationActor_SupervisorStrategy_IsResume()
    {
        var dmProbe = CreateTestProbe();

        var actorRef = new Akka.TestKit.TestActorRef<SiteCommunicationActorProbe>(
            Sys, Props.Create(() => new SiteCommunicationActorProbe("site1", new CommunicationOptions(), dmProbe.Ref)));

        var strategy = actorRef.UnderlyingActor.GetSupervisorStrategy();

        var oneForOne = Assert.IsType<OneForOneStrategy>(strategy);
        var directive = oneForOne.Decider.Decide(new InvalidOperationException("transient child fault"));
        Assert.Equal(Directive.Resume, directive);
    }
}
