using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
namespace GB5Shared.Telemetry.Middleware;
///
/// Middleware that checks for active diagnostic sessions and elevates tracing/sampling
/// for matching requests — without touching the global OTel configuration.
///
/// Active sessions are cached per tenant for 30 seconds (IMemoryCache) to avoid a
/// DB round-trip on every request. A missed 30s window is acceptable for diagnostics.
///
///
/// Session matching priority:
/// 1. SessionScope=4 (Session): matches LoginDTO.SessionId
/// 2. SessionScope=3 (User): matches LoginDTO.UserId
/// 3. SessionScope=2 (Tenant): matches LoginDTO.ClientId
/// 4. SessionScope=1 (Global): matches any request (emergency only)
///
///
/// Register in Program.cs AFTER UseRouting() and BEFORE the endpoint middleware:
/// app.UseMiddleware<DiagnosticsSessionMiddleware>();
///
///
public sealed class DiagnosticsSessionMiddleware
{
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
private readonly ILogger _logger;
// Cache TTL: 30 seconds — acceptable staleness for diagnostic session activation
private static readonly TimeSpan CacheTtl = TimeSpan.FromSeconds(30);
public DiagnosticsSessionMiddleware(
RequestDelegate next,
IMemoryCache cache,
ILogger logger)
{
_next = next;
_cache = cache;
_logger = logger;
}
public async Task InvokeAsync(HttpContext ctx)
{
// Check for active support token in header
var supportToken = ctx.Request.Headers["X-Support-Token"].FirstOrDefault();
// Read tenant/user/session context from Activity tags (set by BaseEndpoint after _next)
// For pre-execution context, try reading Login header if present
var tenantIdStr = ctx.Request.Headers["X-Tenant-Id"].FirstOrDefault();
var userIdStr = ctx.Request.Headers["X-User-Id"].FirstOrDefault();
var sessionIdStr = ctx.Request.Headers["X-Session-Id"].FirstOrDefault();
if (!string.IsNullOrEmpty(supportToken))
{
// Mark token presence on the activity for ObservabilityWriter
Activity.Current?.SetTag("gb5.support.token", supportToken[..Math.Min(8, supportToken.Length)] + "...");
}
await _next(ctx);
// Post-request: check if any diagnostic session was active for this request.
// Activity tags are now populated by BaseEndpoint.
var activity = Activity.Current;
if (activity is null) return;
int.TryParse(activity.GetTagItem("gb5.client.id") as string, out var clientId);
if (clientId <= 0) return;
var cacheKey = $"DiagSessions:{clientId}";
var hasActiveSessions = _cache.TryGetValue(cacheKey, out bool active) ? active : false;
if (hasActiveSessions)
activity.SetTag("gb5.diagnostic.session_active", true);
}
}