using System.Text.Json; namespace TornadoAce_CJOnStyle.Services { public sealed class PocRunLogWriter { private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; public string Write(PocRunResult result) { var logDirectory = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TornadoAce_CJOnStyle", "Logs"); Directory.CreateDirectory(logDirectory); var fileName = $"{DateTime.Now:yyyyMMdd-HHmmss}-{SanitizeFileName(result.Selection.Record.CueSheetId)}.json"; var logPath = Path.Combine(logDirectory, fileName); var entry = PocRunLogEntry.From(result); File.WriteAllText(logPath, JsonSerializer.Serialize(entry, JsonOptions)); return logPath; } private static string SanitizeFileName(string value) { var invalidChars = Path.GetInvalidFileNameChars(); var chars = value.Select(character => invalidChars.Contains(character) ? '_' : character).ToArray(); return new string(chars); } } public sealed record PocRunLogEntry( DateTime CreatedAt, string CueSheetId, string ProgramTitle, string? ErpProgramCode, string SceneName, double AverageMatchRate, int ReviewCount, int LlmRequestCount, IReadOnlyList MappingChecks, IReadOnlyList AdapterChecks, IReadOnlyList LlmAuditSummaries) { public static PocRunLogEntry From(PocRunResult result) { return new PocRunLogEntry( DateTime.Now, result.Selection.Record.CueSheetId, result.Selection.Record.ProgramTitle, result.Schedule?.ProgramCode, result.GeneratedScene.SceneName, result.InspectionSummary.AverageMatchRate, result.InspectionSummary.ReviewCount, result.LlmReviewRequests.Count, result.MappingValidations.Select(check => $"{check.CheckId}:{check.Status}:{check.Detail}").ToArray(), result.AdapterChecks.Select(check => $"{check.CheckId}:{check.Status}:{check.Evidence}").ToArray(), result.LlmReviewRequests.Select(request => request.AuditSummary).ToArray()); } } }