using QuantEngine.Core.Interfaces; using QuantEngine.Application.Services; namespace QuantEngine.Web.Endpoints; public static class CollectionEndpoints { public static void MapCollectionEndpoints(this WebApplication app) { var group = app.MapGroup("/api/collection") .WithName("Collection"); group.MapGet("/state", GetCollectionState) .WithName("GetCollectionState") .Produces(200) .Produces(500); group.MapGet("/runs", GetRecentRuns) .WithName("GetRecentRuns") .Produces(200) .Produces(500); group.MapGet("/runs/{runId}/snapshots", GetRunSnapshots) .WithName("GetRunSnapshots") .Produces(200) .Produces(404) .Produces(500); group.MapGet("/runs/{runId}/errors", GetRunErrors) .WithName("GetRunErrors") .Produces(200) .Produces(404) .Produces(500); group.MapGet("/latest/{ticker}", GetLatestSnapshotsForTicker) .WithName("GetLatestSnapshotsForTicker") .Produces(200) .Produces(500); group.MapPost("/run", StartCollectionRun) .WithName("StartCollectionRun") .Produces(202) .Produces(500); } private static async Task GetCollectionState(ICollectionRepository repo) { try { var state = await repo.GetDashboardStateAsync(); return Results.Ok(state); } catch { return Results.StatusCode(500); } } private static async Task GetRecentRuns(ICollectionRepository repo, int limit = 20) { try { var runs = await repo.GetRecentRunsAsync(limit); return Results.Ok(new { runs, count = runs.Count }); } catch { return Results.StatusCode(500); } } private static async Task GetRunSnapshots(string runId, ICollectionRepository repo) { try { var snapshots = await repo.GetRunSnapshotsAsync(runId); return Results.Ok(new { runId, snapshots, count = snapshots.Count }); } catch { return Results.StatusCode(500); } } private static async Task GetRunErrors(string runId, ICollectionRepository repo, int limit = 50) { try { var errors = await repo.GetRunErrorsAsync(runId, limit); return Results.Ok(new { runId, errors, count = errors.Count }); } catch { return Results.StatusCode(500); } } private static async Task GetLatestSnapshotsForTicker(string ticker, ICollectionRepository repo, int limit = 10) { try { var snapshots = await repo.GetLatestSnapshotsForTickerAsync(ticker, limit); return Results.Ok(new { ticker, snapshots, count = snapshots.Count }); } catch { return Results.StatusCode(500); } } private static async Task StartCollectionRun( DataCollectionService collectionService, HttpRequest request, ILogger logger) { try { var runId = Guid.NewGuid().ToString("N"); var now = DateTime.UtcNow.ToString("o"); var body = await request.ReadFromJsonAsync(); var account = body?.Account ?? "real"; var tickers = body?.Tickers ?? new List { "005930", "000660" }; // Trigger async collection (fire-and-forget) _ = Task.Run(async () => { try { await collectionService.RunCollectionAsync(runId, account, tickers); } catch (Exception ex) { logger.LogError(ex, "Collection run {RunId} failed", runId); } }); return Results.Accepted($"/api/collection/runs/{runId}", new { runId, status = "running", startedAt = now, tickerCount = tickers.Count }); } catch (Exception ex) { logger.LogError(ex, "Failed to start collection run"); return Results.StatusCode(500); } } private class CollectionRunRequest { public string? Account { get; set; } public List? Tickers { get; set; } } }