using System.Text.Json; using Microsoft.Extensions.Logging; using QuantEngine.Core.Interfaces; namespace QuantEngine.Application.Services; /// /// JSON-first seed ingestion path. XLSX conversion remains an external /// preparation step; the runtime application only reads canonical JSON and /// persists normalized snapshots to PostgreSQL. /// public sealed class JsonSeedIngestionService { private readonly GatherTradingDataParser _parser; private readonly ICollectionWriteRepository _repository; private readonly ILogger _logger; public JsonSeedIngestionService( GatherTradingDataParser parser, ICollectionWriteRepository repository, ILogger logger) { _parser = parser; _repository = repository; _logger = logger; } public async Task IngestAsync(string jsonPath, string runId) { var startedAt = DateTimeOffset.UtcNow; var rows = _parser.ParseGatherTradingData(jsonPath); await _repository.SaveRunAsync(new CollectionRunRecord( runId, "RUNNING", startedAt.ToString("O"), null, rows.Count, 0)); var errors = 0; foreach (var row in rows) { if (!row.TryGetValue("Ticker", out var tickerValue) || string.IsNullOrWhiteSpace(tickerValue?.ToString())) { errors++; continue; } var ticker = tickerValue.ToString()!; await _repository.SaveSnapshotAsync(new CollectionSnapshotRecord( runId, "data_feed", ticker, "json_seed", JsonSerializer.Serialize(row), startedAt.ToString("O"))); } var finishedAt = DateTimeOffset.UtcNow; var status = rows.Count > 0 && errors == 0 ? "COMPLETED" : "COMPLETED_WITH_ERRORS"; await _repository.UpdateRunStatusAsync(runId, status, finishedAt.ToString("O"), rows.Count - errors, errors); _logger.LogInformation("JSON seed ingestion {RunId} completed: {Snapshots} snapshots, {Errors} errors", runId, rows.Count - errors, errors); return new CollectionRunResult { RunId = runId, Status = status, StartedAt = startedAt.ToString("O"), FinishedAt = finishedAt.ToString("O"), SuccessCount = rows.Count - errors, ErrorCount = errors, Rows = rows }; } }