using Microsoft.Extensions.Logging.Abstractions;
using ZB.MOM.WW.MxGateway.Server.Configuration;
using ZB.MOM.WW.MxGateway.Server.Dashboard.Hubs;
using ZB.MOM.WW.MxGateway.Server.Grpc;
using ZB.MOM.WW.MxGateway.Server.Metrics;

namespace ZB.MOM.WW.MxGateway.Server.Sessions;

/// <summary>
///     Dependencies a <see cref="GatewaySession"/> needs to construct and own its
///     <see cref="SessionEventDistributor"/>. Bundled so the session constructor stays a
///     single optional parameter rather than four, and so unit tests that build a session
///     directly get a working distributor from <see cref="Default"/> without wiring DI.
/// </summary>
/// <param name="Mapper">
///     Maps worker IPC <c>WorkerEvent</c> frames to public <c>MxEvent</c>s. The distributor
///     pump applies this once per event in worker order, mirroring the mapping
///     <c>EventStreamService.ProduceEventsAsync</c> used previously.
/// </param>
/// <param name="EventOptions">
///     Supplies the distributor's per-subscriber queue capacity and replay ring-buffer
///     bounds (<see cref="EventOptions.QueueCapacity"/>,
///     <see cref="EventOptions.ReplayBufferCapacity"/>,
///     <see cref="EventOptions.ReplayRetentionSeconds"/>).
/// </param>
/// <param name="DistributorLogger">Logger for the distributor pump lifecycle.</param>
/// <param name="TimeProvider">Clock used to timestamp and age-evict replay entries.</param>
/// <param name="Metrics">
///     Gateway metrics sink used by the session's per-subscriber overflow handler to record
///     the queue-overflow counter and, for legacy single-subscriber FailFast, the session
///     fault. Carrying it here keeps the distributor decoupled from the metrics type while
///     preserving the observability the pre-epic per-RPC overflow path emitted.
/// </param>
/// <param name="DashboardBroadcaster">
///     Sink the session's internal dashboard mirror loop publishes raw session
///     <c>MxEvent</c>s to. When non-null the session registers an internal distributor
///     subscriber on becoming Ready and mirrors every fanned event to the dashboard
///     EventsHub group regardless of whether a gRPC client is streaming. When null
///     (unit tests that don't exercise the dashboard mirror) no mirror is started.
/// </param>
/// <param name="AllowMultipleEventSubscribers">
///     The session's effective multi-subscriber mode. Carried here so the session
///     can pass it to its <see cref="SessionEventDistributor"/> at construction — the
///     distributor is created at <c>MarkReady</c> (for the dashboard mirror) before any gRPC
///     subscriber attaches, so the mode cannot be learned from a later
///     <c>AttachEventSubscriber</c> call. The distributor gates its FailFast session-fault
///     decision on this mode (single-subscriber only) instead of a live count snapshot,
///     closing the false-FailFast race. Defaults to <see langword="false"/>
///     (single-subscriber) so existing call sites and unit tests are unchanged.
/// </param>
public sealed record SessionEventStreaming(
    MxAccessGrpcMapper Mapper,
    EventOptions EventOptions,
    ILogger<SessionEventDistributor> DistributorLogger,
    TimeProvider TimeProvider,
    GatewayMetrics Metrics,
    IDashboardEventBroadcaster? DashboardBroadcaster = null,
    bool AllowMultipleEventSubscribers = false)
{
    /// <summary>
    ///     Defaults used when a session is constructed without explicit streaming
    ///     dependencies (unit tests). Uses a fresh mapper, default event options, a no-op
    ///     logger, the system clock, a fresh metrics sink, and no dashboard mirror.
    /// </summary>
    public static SessionEventStreaming Default { get; } = new(
        new MxAccessGrpcMapper(),
        new EventOptions(),
        NullLogger<SessionEventDistributor>.Instance,
        TimeProvider.System,
        new GatewayMetrics());
}
