using Akka.Actor;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.OtOpcUa.Cluster;
using ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet;
using ZB.MOM.WW.OtOpcUa.Commons.Types;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Clients;

/// <summary>
/// <see cref="IFleetDiagnosticsClient"/> backed by an Akka <see cref="ActorSelection"/> against
/// the target node's <c>DriverHostActor</c> at <c>akka.tcp://otopcua@&lt;host&gt;:&lt;port&gt;/user/driver-host</c>.
/// Sends <see cref="GetDiagnostics"/>; expects a <see cref="NodeDiagnosticsSnapshot"/> reply.
///
/// On timeout or any other failure (peer down, GetDiagnostics handler missing) returns an
/// empty snapshot so the UI degrades to "no data" instead of throwing.
/// </summary>
public sealed class FleetDiagnosticsClient : IFleetDiagnosticsClient
{
    private static readonly TimeSpan AskTimeout = TimeSpan.FromSeconds(3);

    private readonly ActorSystem _system;
    private readonly string _systemName;

    /// <summary>Initializes a new FleetDiagnosticsClient with the given actor system and cluster options.</summary>
    /// <param name="system">The Akka actor system.</param>
    /// <param name="options">Cluster configuration options.</param>
    public FleetDiagnosticsClient(ActorSystem system, IOptions<AkkaClusterOptions> options)
    {
        _system = system;
        _systemName = options.Value.SystemName;
    }

    /// <inheritdoc />
    public async Task<NodeDiagnosticsSnapshot> GetDiagnosticsAsync(NodeId nodeId, CancellationToken ct)
    {
        var selection = _system.ActorSelection($"akka.tcp://{_systemName}@{nodeId.Value}/user/driver-host");
        try
        {
            using var linked = CancellationTokenSource.CreateLinkedTokenSource(ct);
            linked.CancelAfter(AskTimeout);
            return await selection.Ask<NodeDiagnosticsSnapshot>(
                new GetDiagnostics(CorrelationId.NewId()), AskTimeout, linked.Token);
        }
        catch (Exception)
        {
            return new NodeDiagnosticsSnapshot(
                nodeId,
                CurrentRevision: null,
                Drivers: Array.Empty<DriverInstanceDiagnostics>(),
                AsOfUtc: DateTime.UtcNow);
        }
    }
}
