using Microsoft.Extensions.Logging;
using ZB.MOM.WW.MxGateway.Client;
using ZB.MOM.WW.MxGateway.Contracts.Proto;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;

/// <summary>
///     Production <see cref="IGalaxyAlarmAcknowledger"/> backed by the session-less
///     <c>MxGatewayClient.AcknowledgeAlarmAsync</c> RPC. The updated gateway routes
///     acknowledgement through its always-on central alarm monitor, so no worker
///     session is involved — the driver supplies only the alarm reference, comment,
///     and operator principal.
/// </summary>
/// <remarks>
///     A non-OK <see cref="ProtocolStatus"/> means the gateway never reached MXAccess
///     (transport / dispatch failure) and is surfaced as a thrown exception. A non-zero
///     native ack return code (<c>hresult</c>) means MXAccess itself rejected the ack;
///     that is logged as a warning rather than thrown so a transient MXAccess hiccup
///     doesn't block the operator workflow — the operator can retry.
/// </remarks>
internal sealed class GatewayGalaxyAlarmAcknowledger : IGalaxyAlarmAcknowledger
{
    private readonly MxGatewayClient _client;
    private readonly ILogger _logger;

    /// <summary>Initializes a new instance of the <see cref="GatewayGalaxyAlarmAcknowledger"/> class.</summary>
    /// <param name="client">The MX gateway client used to send acknowledgments.</param>
    /// <param name="logger">A logger for diagnostic output.</param>
    public GatewayGalaxyAlarmAcknowledger(MxGatewayClient client, ILogger logger)
    {
        _client = client ?? throw new ArgumentNullException(nameof(client));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <inheritdoc />
    public async Task AcknowledgeAsync(
        string alarmFullReference,
        string comment,
        string operatorUser,
        CancellationToken cancellationToken)
    {
        ArgumentException.ThrowIfNullOrEmpty(alarmFullReference);

        var reply = await _client.AcknowledgeAlarmAsync(
            new AcknowledgeAlarmRequest
            {
                ClientCorrelationId = Guid.NewGuid().ToString("N"),
                AlarmFullReference = alarmFullReference,
                Comment = comment ?? string.Empty,
                OperatorUser = operatorUser ?? string.Empty,
            },
            cancellationToken).ConfigureAwait(false);

        // Protocol status — the gateway failed before MXAccess saw the ack. This is a
        // hard failure: the operator's request was not delivered at all.
        if (reply.ProtocolStatus is { } proto && proto.Code != ProtocolStatusCode.Ok)
        {
            throw new InvalidOperationException(
                $"Galaxy AcknowledgeAlarm for '{alarmFullReference}' failed at the gateway: "
                + $"{proto.Code} {proto.Message}");
        }

        // hresult is the authoritative native ack return code (0 = success). It is
        // absent only on a worker protocol violation; with an OK protocol status a
        // missing value is treated as success.
        if (reply.HasHresult && reply.Hresult != 0)
        {
            _logger.LogWarning(
                "Galaxy AcknowledgeAlarm for {AlarmRef} returned native ack failure code {Hresult}.",
                alarmFullReference, reply.Hresult);
        }
    }
}
