using System.Globalization;
using System.Text;

namespace ZB.MOM.WW.ScadaBridge.LoadHarness;

/// <summary>
/// Renders a <see cref="HarnessRunResult"/> as the markdown table that goes into the
/// results document, so the published numbers and the JSON come from one source.
/// </summary>
public static class ResultsFormatter
{
    /// <summary>Formats a run result for console output and the results doc.</summary>
    /// <param name="result">The run to format.</param>
    /// <returns>A markdown-ish plain-text report.</returns>
    public static string Format(HarnessRunResult result)
    {
        var culture = CultureInfo.InvariantCulture;
        var builder = new StringBuilder();

        builder.AppendLine("=== MEASURED ===");
        builder.AppendLine(culture, $"host                    {result.Environment.MachineName} / {result.Environment.OsDescription}");
        builder.AppendLine(culture, $"cpus / runtime / gc     {result.Environment.ProcessorCount} / {result.Environment.RuntimeVersion} / serverGC={result.Environment.ServerGc}");
        builder.AppendLine(culture, $"scale                   {result.Config.Sites} sites x {result.Config.InstancesPerSite} instances x {result.Config.TagsPerInstance} tags = {result.Config.TotalSubscriptions:N0} subscriptions");
        builder.AppendLine(culture, $"total run               {result.TotalSeconds:F1}s");
        builder.AppendLine();

        builder.AppendLine("-- deployment / ramp --");
        builder.AppendLine(culture, $"site fixtures built     {result.SiteRampSeconds:F1}s");
        builder.AppendLine(culture, $"all instance actors     {result.InstanceRampSeconds:F1}s");
        builder.AppendLine(culture, $"slowest single site     {result.SlowestSiteInstanceRampSeconds:F1}s  ({result.Config.InstancesPerSite} instances)");
        builder.AppendLine();

        builder.AppendLine("-- offered load (steady-state window) --");
        builder.AppendLine(culture, $"nominal                 {result.NominalUpdatesPerSecond:N0} updates/s");
        builder.AppendLine(culture, $"achieved                {result.AchievedUpdatesPerSecond:N0} updates/s ({result.AchievedUpdatesPerSecond / result.NominalUpdatesPerSecond:P1} of nominal)");
        builder.AppendLine(culture, $"updates offered         {result.SteadyStateEmittedTagUpdates:N0}");
        builder.AppendLine(culture, $"driver slice overrun    {result.DriverLagSeconds:F1}s cumulative");
        builder.AppendLine(culture, $"skipped (no callback)   {result.DriverSkippedNoCallback:N0}");
        builder.AppendLine();

        builder.AppendLine("-- tag update latency (DCL boundary -> stream subscriber) --");
        AppendLatency(builder, culture, result.TagUpdateLatency);
        builder.AppendLine();

        builder.AppendLine("-- live stream subscribers (steady-state window) --");
        builder.AppendLine(culture, $"events delivered        {result.StreamProbeReceived:N0}");
        builder.AppendLine(culture, $"events dropped          {result.StreamProbeDropped:N0}");
        builder.AppendLine();

        builder.AppendLine("-- health report delivery --");
        AppendLatency(builder, culture, result.HealthReportLatency);
        builder.AppendLine(culture, $"reports ingested        {result.HealthReportsDelivered:N0}");
        builder.AppendLine(culture, $"sites tracked centrally {result.SitesTrackedByAggregator}");
        builder.AppendLine();

        builder.AppendLine("-- debug view snapshot (under load) --");
        AppendLatency(builder, culture, result.DebugSnapshotLatency);
        builder.AppendLine(culture, $"completed / timed out   {result.DebugSnapshotsCompleted:N0} / {result.DebugSnapshotTimeouts:N0}");
        builder.AppendLine();

        if (result.SteadyStateResources is { } steady)
        {
            builder.AppendLine("-- resources (steady-state window) --");
            builder.AppendLine(culture, $"window                  {steady.DurationSeconds:F0}s over {steady.SampleCount} samples");
            builder.AppendLine(culture, $"working set             {steady.WorkingSetStartMb:F0} -> {steady.WorkingSetEndMb:F0} MB (peak {steady.WorkingSetPeakMb:F0} MB)");
            builder.AppendLine(culture, $"working set slope       {steady.WorkingSetSlopeMbPerMinute:F2} MB/min");
            builder.AppendLine(culture, $"managed heap            {steady.ManagedHeapStartMb:F0} -> {steady.ManagedHeapEndMb:F0} MB (peak {steady.ManagedHeapPeakMb:F0} MB)");
            builder.AppendLine(culture, $"managed heap slope      {steady.ManagedHeapSlopeMbPerMinute:F2} MB/min");
            builder.AppendLine(culture, $"cpu mean / peak         {steady.MeanCpuPercentOfOneCore:F0}% / {steady.PeakCpuPercentOfOneCore:F0}% of one core ({steady.MeanCpuPercentOfOneCore / result.Environment.ProcessorCount:F1}% of the box)");
            builder.AppendLine(culture, $"threads / gen2 GCs      {steady.MeanThreadCount:F0} / {steady.Gen2Collections}");
            builder.AppendLine();
        }

        if (result.StoreAndForwardDrain is { } drain)
        {
            builder.AppendLine("-- store-and-forward (register row 50) --");
            builder.AppendLine(culture, $"messages                {drain.MessageCount:N0}");
            builder.AppendLine(culture, $"concurrent buffering    {drain.EnqueuePerSecond:N0} msg/s ({drain.EnqueueSeconds:F1}s)");
            builder.AppendLine(culture, $"retry wait before drain {drain.TimeToFirstDeliverySeconds:F1}s (DefaultRetryInterval)");
            builder.AppendLine(culture, $"drain throughput        {drain.DrainPerSecond:N0} msg/s (active drain {drain.DrainSeconds - drain.TimeToFirstDeliverySeconds:F2}s)");
            builder.AppendLine(culture, $"residual depth          {drain.ResidualDepth}");
            builder.AppendLine();
        }

        if (result.SlowSubscriber is { } slow)
        {
            builder.AppendLine("-- slow-subscriber isolation (register row 50) --");
            builder.AppendLine(culture, $"published               {slow.PublishedEvents:N0} at {slow.PublishPerSecond:N0}/s");
            builder.AppendLine(culture, $"healthy min delivery    {slow.HealthyMinDeliveryRatio:P2}");
            builder.AppendLine(culture, $"stalled delivery        {slow.SlowDeliveryRatio:P2}");
            foreach (var outcome in slow.Outcomes)
            {
                builder.AppendLine(culture,
                    $"  {outcome.Name,-28} {(outcome.IsSlow ? "STALLED" : "healthy"),-8} " +
                    $"recv {outcome.Received,8:N0}  dropped {outcome.Dropped,8:N0}  ratio {outcome.DeliveryRatio:P2}");
            }

            builder.AppendLine();
        }

        return builder.ToString();
    }

    private static void AppendLatency(StringBuilder builder, CultureInfo culture, Metrics.LatencySnapshot snapshot)
    {
        builder.AppendLine(culture,
            $"samples {snapshot.Count:N0}  mean {snapshot.MeanMs:F2}ms  p50 {snapshot.P50Ms:F2}ms  " +
            $"p95 {snapshot.P95Ms:F2}ms  p99 {snapshot.P99Ms:F2}ms  p99.9 {snapshot.P999Ms:F2}ms  max {snapshot.MaxMs:F2}ms");
    }
}
