using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;

namespace ZB.MOM.WW.MxGateway.Server.Dashboard.Hubs;

/// <summary>
/// SignalR hub for per-session MxEvent push. Clients call
/// <see cref="SubscribeSession"/> to join the group for a specific
/// session; the dashboard's MxEvent broadcaster (a future hook on
/// <c>EventStreamService</c>) sends messages to <c>session:{id}</c>.
/// </summary>
/// <remarks>
/// The publisher side is intentionally a follow-up. Today the dashboard's
/// per-session event view is fed by the snapshot hub, which carries the
/// rolling recent-events list. Once a dedicated MxEvent broadcaster
/// lands, this hub's group convention is what it will publish to.
/// </remarks>
[Authorize(Policy = DashboardAuthenticationDefaults.HubClientsPolicy)]
public sealed class EventsHub : Hub
{
    /// <summary>Method name used to push individual <c>MxEvent</c> values to clients.</summary>
    public const string EventMessage = "MxEvent";

    public static string GroupName(string sessionId) => $"session:{sessionId}";

    /// <summary>
    /// Subscribes the calling SignalR connection to the per-session events
    /// group, so that events broadcast by
    /// <see cref="DashboardEventBroadcaster"/> for that session reach this
    /// client.
    /// </summary>
    /// <remarks>
    /// Server-038: in v1 the hub-level <see cref="AuthorizeAttribute"/>
    /// (<c>HubClientsPolicy</c>) only checks that the caller carries one of
    /// the dashboard roles (Admin or Viewer); both roles may subscribe to
    /// any session id they choose. This is acceptable today because (a) the
    /// dashboard's per-session views show non-secret session metadata that
    /// any authenticated dashboard user can already see, and (b) value
    /// logging in the source gRPC stream is gated by the same redaction
    /// policy that protects logs. The per-session ACL that gates the gRPC
    /// <c>StreamEvents</c> RPC is intentionally not yet mirrored here.
    /// TODO(per-session-acl): once a role/scope is introduced that scopes a
    /// Viewer to a specific session or tenant, add a session-access check
    /// at this seam — either inline (consult the per-user allowed-session
    /// set on <c>Context.User</c> claims / <c>Context.Items</c>) or via a
    /// dedicated authorization policy applied to the hub method itself.
    /// </remarks>
    /// <param name="sessionId">Session id to subscribe the caller to.</param>
    public Task SubscribeSession(string sessionId)
    {
        if (string.IsNullOrWhiteSpace(sessionId))
        {
            return Task.CompletedTask;
        }

        return Groups.AddToGroupAsync(Context.ConnectionId, GroupName(sessionId));
    }

    public Task UnsubscribeSession(string sessionId)
    {
        if (string.IsNullOrWhiteSpace(sessionId))
        {
            return Task.CompletedTask;
        }

        return Groups.RemoveFromGroupAsync(Context.ConnectionId, GroupName(sessionId));
    }
}
