using Akka.Actor;
using Akka.TestKit.Xunit2;
using ScadaLink.Commons.Messages.Deployment;
using ScadaLink.Commons.Messages.Lifecycle;
using ScadaLink.Commons.Messages.Integration;
using ScadaLink.Commons.Messages.RemoteQuery;
using ScadaLink.Communication.Actors;

namespace ScadaLink.Communication.Tests;

/// <summary>
/// WP-4: Tests for SiteCommunicationActor message routing to local actors.
/// </summary>
public class SiteCommunicationActorTests : TestKit
{
    private readonly CommunicationOptions _options = new();

    public SiteCommunicationActorTests()
        : base(@"akka.loglevel = DEBUG")
    {
    }

    [Fact]
    public void DeployCommand_ForwardedToDeploymentManager()
    {
        var dmProbe = CreateTestProbe();
        var siteActor = Sys.ActorOf(Props.Create(() =>
            new SiteCommunicationActor("site1", _options, dmProbe.Ref)));

        var command = new DeployInstanceCommand(
            "dep1", "inst1", "hash1", "{}", "admin", DateTimeOffset.UtcNow);
        siteActor.Tell(command);

        dmProbe.ExpectMsg<DeployInstanceCommand>(msg => msg.DeploymentId == "dep1");
    }

    [Fact]
    public void LifecycleCommands_ForwardedToDeploymentManager()
    {
        var dmProbe = CreateTestProbe();
        var siteActor = Sys.ActorOf(Props.Create(() =>
            new SiteCommunicationActor("site1", _options, dmProbe.Ref)));

        siteActor.Tell(new DisableInstanceCommand("cmd1", "inst1", DateTimeOffset.UtcNow));
        dmProbe.ExpectMsg<DisableInstanceCommand>();

        siteActor.Tell(new EnableInstanceCommand("cmd2", "inst1", DateTimeOffset.UtcNow));
        dmProbe.ExpectMsg<EnableInstanceCommand>();

        siteActor.Tell(new DeleteInstanceCommand("cmd3", "inst1", DateTimeOffset.UtcNow));
        dmProbe.ExpectMsg<DeleteInstanceCommand>();
    }

    [Fact]
    public void DeploymentStateQuery_ForwardedToDeploymentManager()
    {
        // DeploymentManager-006: the site-before-redeploy query travels over the
        // ClusterClient command/control transport and is routed to the local
        // Deployment Manager, which owns the deployed-config store.
        var dmProbe = CreateTestProbe();
        var siteActor = Sys.ActorOf(Props.Create(() =>
            new SiteCommunicationActor("site1", _options, dmProbe.Ref)));

        var request = new DeploymentStateQueryRequest("corr-q", "inst1", DateTimeOffset.UtcNow);
        siteActor.Tell(request);

        dmProbe.ExpectMsg<DeploymentStateQueryRequest>(msg => msg.CorrelationId == "corr-q");
    }

    [Fact]
    public void IntegrationCall_WithoutHandler_ReturnsFailure()
    {
        var dmProbe = CreateTestProbe();
        var siteActor = Sys.ActorOf(Props.Create(() =>
            new SiteCommunicationActor("site1", _options, dmProbe.Ref)));

        var request = new IntegrationCallRequest(
            "corr1", "site1", "inst1", "ExtSys1", "GetData",
            new Dictionary<string, object?>(), DateTimeOffset.UtcNow);

        siteActor.Tell(request);

        ExpectMsg<IntegrationCallResponse>(msg =>
            !msg.Success && msg.ErrorMessage == "Integration handler not available");
    }

    [Fact]
    public void IntegrationCall_WithHandler_ForwardedToHandler()
    {
        var dmProbe = CreateTestProbe();
        var handlerProbe = CreateTestProbe();
        var siteActor = Sys.ActorOf(Props.Create(() =>
            new SiteCommunicationActor("site1", _options, dmProbe.Ref)));

        // Register integration handler
        siteActor.Tell(new RegisterLocalHandler(LocalHandlerType.Integration, handlerProbe.Ref));

        var request = new IntegrationCallRequest(
            "corr1", "site1", "inst1", "ExtSys1", "GetData",
            new Dictionary<string, object?>(), DateTimeOffset.UtcNow);

        siteActor.Tell(request);
        handlerProbe.ExpectMsg<IntegrationCallRequest>(msg => msg.CorrelationId == "corr1");
    }

    [Fact]
    public void EventLogQuery_WithoutHandler_ReturnsFailure()
    {
        var dmProbe = CreateTestProbe();
        var siteActor = Sys.ActorOf(Props.Create(() =>
            new SiteCommunicationActor("site1", _options, dmProbe.Ref)));

        var request = new EventLogQueryRequest(
            "corr1", "site1", null, null, null, null, null, null, null, 25, DateTimeOffset.UtcNow);

        siteActor.Tell(request);

        ExpectMsg<EventLogQueryResponse>(msg => !msg.Success);
    }
}
