using FastEndpoints; using KArtSell.BuildingBlocks.Time; using Microsoft.Extensions.Logging; namespace KArtSell.Host.Features.ShadowRun; /// /// POST /api/shadow-runs /// Initiate a 252+ trading-day model validation run. /// Returns 202 Accepted with job tracking info. /// public class InitiateShadowRunEndpoint : Endpoint { private InitiateShadowRunHandler? _handler; private ILogger? _logger; private static readonly Action LogRequestReceived = LoggerMessage.Define( LogLevel.Information, new EventId(1, nameof(LogRequestReceived)), "Shadow run request received: {ModelId}"); private static readonly Action LogRequestFailed = LoggerMessage.Define( LogLevel.Error, new EventId(2, nameof(LogRequestFailed)), "Shadow run initiation failed"); public override void Configure() { Post("/api/shadow-runs"); Roles("Admin", "Researcher"); // RBAC: Only Admin or Researcher can initiate Validator(); } public override async Task HandleAsync(InitiateShadowRunRequest req, CancellationToken ct) { _handler = Resolve(); _logger = Resolve>(); var correlationId = HttpContext.Items["CorrelationId"] as Guid? ?? Guid.NewGuid(); try { LogRequestReceived(_logger, req.ModelId, null); var response = await _handler.HandleAsync(req, correlationId, ct); // 202 Accepted: Job queued, results available later via polling HttpContext.Response.StatusCode = 202; await HttpContext.Response.WriteAsJsonAsync(response, ct); } catch (InvalidOperationException ex) { ThrowError(ex.Message, 400); } catch (Exception ex) { LogRequestFailed(_logger, ex); ThrowError("Shadow run initiation failed. Please retry.", 500); } } }