using Akka.Actor;
using Akka.TestKit.Xunit2;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ScadaLink.Commons.Messages.Deployment;

namespace ScadaLink.Communication.Tests;

/// <summary>
/// WP-2: Tests for CommunicationService initialization and state.
/// </summary>
public class CommunicationServiceTests : TestKit
{
    [Fact]
    public async Task BeforeInitialization_ThrowsOnUsage()
    {
        var options = Options.Create(new CommunicationOptions());
        var logger = NullLogger<CommunicationService>.Instance;
        var service = new CommunicationService(options, logger);

        // CommunicationService requires SetCommunicationActor before use
        await Assert.ThrowsAsync<InvalidOperationException>(() =>
            service.DeployInstanceAsync("site1",
                new DeployInstanceCommand(
                    "dep1", "inst1", "hash1", "{}", "admin", DateTimeOffset.UtcNow)));
    }

    [Fact]
    public void UnsubscribeDebugView_IsTellNotAsk()
    {
        // Verify the method signature is void (fire-and-forget Tell pattern)
        var method = typeof(CommunicationService).GetMethod("UnsubscribeDebugView");
        Assert.NotNull(method);
        Assert.Equal(typeof(void), method!.ReturnType);
    }

    // ── DeploymentManager-006: query-the-site-before-redeploy ──

    [Fact]
    public async Task QueryDeploymentStateAsync_BeforeInitialization_Throws()
    {
        var service = new CommunicationService(
            Options.Create(new CommunicationOptions()),
            NullLogger<CommunicationService>.Instance);

        await Assert.ThrowsAsync<InvalidOperationException>(() =>
            service.QueryDeploymentStateAsync("site1",
                new DeploymentStateQueryRequest("corr-1", "inst1", DateTimeOffset.UtcNow)));
    }

    [Fact]
    public async Task QueryDeploymentStateAsync_SendsEnvelopeAndReturnsResponse()
    {
        // The query must be dispatched as a SiteEnvelope over the existing
        // command/control transport, exactly like other site-directed commands,
        // and the typed response returned to the caller.
        var service = new CommunicationService(
            Options.Create(new CommunicationOptions()),
            NullLogger<CommunicationService>.Instance);

        // A probe stands in for CentralCommunicationActor: it asserts the
        // envelope shape and replies with a typed response.
        var commActor = Sys.ActorOf(Props.Create(() => new EchoStateQueryActor()));
        service.SetCommunicationActor(commActor);

        var request = new DeploymentStateQueryRequest("corr-9", "QueriedInst", DateTimeOffset.UtcNow);
        var response = await service.QueryDeploymentStateAsync("site-a", request);

        Assert.Equal("corr-9", response.CorrelationId);
        Assert.Equal("QueriedInst", response.InstanceUniqueName);
        Assert.True(response.IsDeployed);
        Assert.Equal("sha256:applied", response.AppliedRevisionHash);
    }

    /// <summary>
    /// Stand-in for CentralCommunicationActor: verifies the message is wrapped
    /// in a SiteEnvelope targeting the requested site and replies with a typed
    /// DeploymentStateQueryResponse.
    /// </summary>
    private class EchoStateQueryActor : ReceiveActor
    {
        public EchoStateQueryActor()
        {
            Receive<SiteEnvelope>(env =>
            {
                if (env is { SiteId: "site-a", Message: DeploymentStateQueryRequest req })
                {
                    Sender.Tell(new DeploymentStateQueryResponse(
                        req.CorrelationId, req.InstanceUniqueName, true,
                        "dep-applied", "sha256:applied", DateTimeOffset.UtcNow));
                }
            });
        }
    }
}
