using System.Text.Json;
using Akka.Actor;
using Akka.TestKit.Xunit2;
using Microsoft.Extensions.Logging.Abstractions;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.DataConnection;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.DebugView;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Flattening;
using ZB.MOM.WW.ScadaBridge.SiteRuntime;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Actors;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Persistence;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Scripts;

namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests.Actors;

/// <summary>
/// Task 16: InstanceActor spawns NativeAlarmActor children from the flattened
/// configuration and surfaces enriched native alarm state in the DebugView snapshot.
/// </summary>
public class InstanceActorNativeAlarmTests : TestKit, IDisposable
{
    private readonly SiteStorageService _storage;
    private readonly ScriptCompilationService _compilationService;
    private readonly SharedScriptLibrary _sharedScriptLibrary;
    private readonly SiteRuntimeOptions _options = new();
    private readonly string _dbFile;

    public InstanceActorNativeAlarmTests()
    {
        _dbFile = Path.Combine(Path.GetTempPath(), $"instance-native-{Guid.NewGuid():N}.db");
        _storage = new SiteStorageService($"Data Source={_dbFile}", NullLogger<SiteStorageService>.Instance);
        _storage.InitializeAsync().GetAwaiter().GetResult();
        _compilationService = new ScriptCompilationService(NullLogger<ScriptCompilationService>.Instance);
        _sharedScriptLibrary = new SharedScriptLibrary(_compilationService, NullLogger<SharedScriptLibrary>.Instance);
    }

    private IActorRef CreateInstanceActorWithDcl(string instanceName, FlattenedConfiguration config, IActorRef dclManager) =>
        ActorOf(Props.Create(() => new InstanceActor(
            instanceName,
            JsonSerializer.Serialize(config),
            _storage,
            _compilationService,
            _sharedScriptLibrary,
            null,
            _options,
            NullLogger<InstanceActor>.Instance,
            dclManager)));

    private static FlattenedConfiguration ConfigWithNativeSource(string instanceName) => new()
    {
        InstanceUniqueName = instanceName,
        NativeAlarmSources =
        [
            new ResolvedNativeAlarmSource
            {
                CanonicalName = "Pressure",
                ConnectionName = "Opc",
                SourceReference = "ns=2;s=T01"
            }
        ]
    };

    [Fact]
    public void SpawnsNativeAlarmActor_WhichSubscribesViaDcl()
    {
        var dcl = CreateTestProbe();
        CreateInstanceActorWithDcl("inst", ConfigWithNativeSource("inst"), dcl.Ref);

        var req = dcl.ExpectMsg<SubscribeAlarmsRequest>();
        Assert.Equal("inst", req.InstanceUniqueName);
        Assert.Equal("Opc", req.ConnectionName);
        Assert.Equal("ns=2;s=T01", req.SourceReference);
    }

    [Fact]
    public void DebugViewSnapshot_IncludesNativeAlarm_AfterTransition()
    {
        var dcl = CreateTestProbe();
        var actor = CreateInstanceActorWithDcl("inst", ConfigWithNativeSource("inst"), dcl.Ref);

        // Simulate the NativeAlarmActor emitting an enriched event upward.
        actor.Tell(new AlarmStateChanged("inst", "T01.Hi", AlarmState.Active, 800, DateTimeOffset.UtcNow)
        {
            Kind = AlarmKind.NativeOpcUa,
            SourceReference = "T01.Hi",
            Condition = new AlarmConditionState(true, false, null, AlarmShelveState.Unshelved, false, 800)
        });

        actor.Tell(new SubscribeDebugViewRequest("inst", "c"));
        var snap = ExpectMsg<DebugViewSnapshot>();

        Assert.Contains(snap.AlarmStates, a =>
            a.SourceReference == "T01.Hi" && a.Kind == AlarmKind.NativeOpcUa && a.Condition.Severity == 800);
    }

    void IDisposable.Dispose()
    {
        Shutdown();
        try { File.Delete(_dbFile); } catch { /* cleanup */ }
    }
}
