using System.Diagnostics;

namespace NATS.Server.JetStream;

/// <summary>
/// Lightweight profiler for JetStream publish hot path.
/// Accumulates per-component timings via Stopwatch ticks.
/// Call <see cref="DumpAndReset"/> to print results.
/// </summary>
public static class JetStreamProfiler
{
    private static long _totalCalls;
    private static long _findBySubjectTicks;
    private static long _getStateTicks;
    private static long _appendTicks;
    private static long _enforcePoliciesTicks;
    private static long _captureOverheadTicks; // total Capture minus sub-components
    private static long _jsonSerializeTicks;
    private static long _ackDeliverTicks;
    private static long _totalProcessMessageTicks;

    public static void RecordFindBySubject(long ticks) => Interlocked.Add(ref _findBySubjectTicks, ticks);
    public static void RecordGetState(long ticks) => Interlocked.Add(ref _getStateTicks, ticks);
    public static void RecordAppend(long ticks) => Interlocked.Add(ref _appendTicks, ticks);
    public static void RecordEnforcePolicies(long ticks) => Interlocked.Add(ref _enforcePoliciesTicks, ticks);
    public static void RecordCaptureOverhead(long ticks) => Interlocked.Add(ref _captureOverheadTicks, ticks);
    public static void RecordJsonSerialize(long ticks) => Interlocked.Add(ref _jsonSerializeTicks, ticks);
    public static void RecordAckDeliver(long ticks) => Interlocked.Add(ref _ackDeliverTicks, ticks);
    public static void RecordTotalProcessMessage(long ticks) => Interlocked.Add(ref _totalProcessMessageTicks, ticks);
    public static void IncrementCalls() => Interlocked.Increment(ref _totalCalls);

    public static string DumpAndReset()
    {
        var calls = Interlocked.Exchange(ref _totalCalls, 0);
        var findBySubject = Interlocked.Exchange(ref _findBySubjectTicks, 0);
        var getState = Interlocked.Exchange(ref _getStateTicks, 0);
        var append = Interlocked.Exchange(ref _appendTicks, 0);
        var enforce = Interlocked.Exchange(ref _enforcePoliciesTicks, 0);
        var overhead = Interlocked.Exchange(ref _captureOverheadTicks, 0);
        var json = Interlocked.Exchange(ref _jsonSerializeTicks, 0);
        var ackDeliver = Interlocked.Exchange(ref _ackDeliverTicks, 0);
        var totalPM = Interlocked.Exchange(ref _totalProcessMessageTicks, 0);

        if (calls == 0) return "No JetStream publish calls recorded.";

        var freq = (double)Stopwatch.Frequency;
        string Ms(long ticks) => $"{ticks / freq * 1000.0:F3} ms";
        string UsPerCall(long ticks) => $"{ticks / freq * 1_000_000.0 / calls:F1} us/call";

        return $"""
            === JetStream Publish Profile ({calls} calls) ===
            Total ProcessMessage:  {Ms(totalPM),12}  {UsPerCall(totalPM),14}
              FindBySubject:       {Ms(findBySubject),12}  {UsPerCall(findBySubject),14}
              GetStateAsync:       {Ms(getState),12}  {UsPerCall(getState),14}
              AppendAsync:         {Ms(append),12}  {UsPerCall(append),14}
              EnforcePolicies:     {Ms(enforce),12}  {UsPerCall(enforce),14}
              Capture overhead:    {Ms(overhead),12}  {UsPerCall(overhead),14}
              JSON serialize:      {Ms(json),12}  {UsPerCall(json),14}
              Ack deliver:         {Ms(ackDeliver),12}  {UsPerCall(ackDeliver),14}
            """;
    }
}
