using Akka.Actor;
using Akka.Hosting;
using ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin;
using ZB.MOM.WW.OtOpcUa.Commons.Types;
using ZB.MOM.WW.OtOpcUa.ControlPlane;

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

/// <summary>
/// <see cref="IAdminOperationsClient"/> backed by the cluster singleton registered in
/// <c>AddOtOpcUaControlPlane</c>. Resolves the singleton proxy from <see cref="ActorRegistry"/>
/// at construction time; each call <c>Ask</c>s the proxy with a 10s timeout.
/// </summary>
public sealed class AdminOperationsClient : IAdminOperationsClient
{
    private static readonly TimeSpan AskTimeout = TimeSpan.FromSeconds(10);

    private readonly IActorRef _proxy;

    /// <summary>Initializes a new instance of the AdminOperationsClient.</summary>
    /// <param name="registry">The actor registry to resolve the admin operations singleton proxy.</param>
    public AdminOperationsClient(ActorRegistry registry)
    {
        _proxy = registry.Get<AdminOperationsActorKey>();
    }

    /// <inheritdoc />
    public async Task<StartDeploymentResult> StartDeploymentAsync(string createdBy, CancellationToken ct)
    {
        var msg = new StartDeployment(createdBy, CorrelationId.NewId());
        using var linked = CancellationTokenSource.CreateLinkedTokenSource(ct);
        linked.CancelAfter(AskTimeout);
        return await _proxy.Ask<StartDeploymentResult>(msg, AskTimeout, linked.Token);
    }

    /// <inheritdoc />
    public async Task<AcknowledgeAlarmResult> AcknowledgeAlarmAsync(
        string alarmId, string user, string? comment, CancellationToken ct)
    {
        var msg = new AcknowledgeAlarmCommand(alarmId, user, comment, CorrelationId.NewId());
        using var linked = CancellationTokenSource.CreateLinkedTokenSource(ct);
        linked.CancelAfter(AskTimeout);
        return await _proxy.Ask<AcknowledgeAlarmResult>(msg, AskTimeout, linked.Token);
    }

    /// <inheritdoc />
    public async Task<ShelveAlarmResult> ShelveAlarmAsync(
        string alarmId, string user, ShelveKind kind, DateTime? unshelveAtUtc, string? comment, CancellationToken ct)
    {
        var msg = new ShelveAlarmCommand(alarmId, user, kind, unshelveAtUtc, comment, CorrelationId.NewId());
        using var linked = CancellationTokenSource.CreateLinkedTokenSource(ct);
        linked.CancelAfter(AskTimeout);
        return await _proxy.Ask<ShelveAlarmResult>(msg, AskTimeout, linked.Token);
    }

    /// <inheritdoc />
    public Task<T> AskAsync<T>(object message, CancellationToken ct)
        => _proxy.Ask<T>(message, cancellationToken: ct);
}
