using System.Text.Json;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace QuantEngine.Application.Services;
///
/// Lightweight startup bootstrap for collection scheduling/readiness.
/// Writes a deterministic artifact so deployment can verify the collection
/// pipeline entry point without forcing a live collection run.
///
public sealed class CollectionBootstrapHostedService : IHostedService
{
private readonly ILogger _logger;
private readonly GatherTradingDataParser _parser;
public CollectionBootstrapHostedService(
ILogger logger,
GatherTradingDataParser parser)
{
_logger = logger;
_parser = parser;
}
public Task StartAsync(CancellationToken cancellationToken)
{
try
{
var repoRoot = FindRepoRoot();
var outputPath = Path.Combine(repoRoot, "Temp", "collection_bootstrap_v1.json");
var tickers = LoadBootstrapTickers();
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
File.WriteAllText(outputPath, JsonSerializer.Serialize(new
{
gate = "PASS",
generated_at_utc = DateTimeOffset.UtcNow,
bootstrap = "collection-scheduling-ready",
ticker_count = tickers.Count,
tickers
}, new JsonSerializerOptions { WriteIndented = true }));
_logger.LogInformation("Collection bootstrap artifact written to {Path}", outputPath);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Collection bootstrap artifact generation failed");
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
private List LoadBootstrapTickers()
{
try
{
var jsonPath = FindGatherTradingDataJson();
if (jsonPath is null)
{
return ["005930"];
}
var data = _parser.ParseGatherTradingData(jsonPath);
return data
.Select(row => row.TryGetValue("Ticker", out var value) ? value?.ToString()?.Trim('"') : null)
.Where(ticker => !string.IsNullOrWhiteSpace(ticker))
.Distinct()
.Take(10)
.ToList()!;
}
catch
{
return ["005930"];
}
}
private static string FindRepoRoot()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null)
{
if (Directory.Exists(Path.Combine(current.FullName, ".git")))
{
return current.FullName;
}
current = current.Parent;
}
return Directory.GetCurrentDirectory();
}
private static string? FindGatherTradingDataJson()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null)
{
var candidate = Path.Combine(current.FullName, "GatherTradingData.json");
if (File.Exists(candidate))
{
return candidate;
}
current = current.Parent;
}
return null;
}
}