using System.Globalization;
namespace Azaion.Missions.E2E.Helpers;
///
/// Appends one row per NFT-PERF / NFT-RES-LIM scenario to a side-channel
/// CSV referenced by an environment variable. The Reporting.Cli converter
/// only knows about compile-time [Trait] data — runtime measurements
/// (P50/P95, MAX_FD, P95_RSS_MiB, etc.) need this separate file so
/// deployment planning + trend dashboards can read them.
///
///
/// File schema (idempotent header written on first append):
/// Timestamp,Category,Scenario,Result,Traces,ErrorMessage
/// The Traces column carries the dynamic key=value pairs the spec requires
/// (e.g., "AC-3.6; P50_MS=23.4; P95_MS=41.8"); the recorder just
/// joins them with semicolons — callers compose the right shape.
///
public sealed class MetricCsvRecorder
{
private readonly string? _path;
private static readonly object Lock = new();
/// name of the env var that carries the target CSV path
/// (e.g., PERF_RESULTS_FILE for NFT-PERF, RESLIM_RESULTS_FILE
/// for NFT-RES-LIM). When the env var is missing or whitespace, every
/// call is a no-op — the recorder is intentionally
/// silent inside the standard CI run.
public MetricCsvRecorder(string envVar)
{
var v = Environment.GetEnvironmentVariable(envVar);
_path = string.IsNullOrWhiteSpace(v) ? null : v;
}
public bool IsEnabled => _path is not null;
public void Record(string category, string scenario, string result, string traces, string? errorMessage = null)
{
if (_path is null) return;
lock (Lock)
{
var dir = Path.GetDirectoryName(_path);
if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
var newFile = !File.Exists(_path);
using var sw = new StreamWriter(_path, append: true);
if (newFile)
sw.WriteLine("Timestamp,Category,Scenario,Result,Traces,ErrorMessage");
sw.WriteLine(
$"{DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)},"
+ $"{Csv(category)},{Csv(scenario)},{Csv(result)},{Csv(traces)},{Csv(errorMessage ?? "")}");
}
}
private static string Csv(string value) =>
value.Contains(',') || value.Contains('"') || value.Contains('\n')
? "\"" + value.Replace("\"", "\"\"", StringComparison.Ordinal) + "\""
: value;
}