using System.Diagnostics.Metrics;

namespace StellaOps.Feedser.Source.Acsc.Internal;

public sealed class AcscDiagnostics : IDisposable
{
    private const string MeterName = "StellaOps.Feedser.Source.Acsc";
    private const string MeterVersion = "1.0.0";

    private readonly Meter _meter;
    private readonly Counter<long> _fetchAttempts;
    private readonly Counter<long> _fetchSuccess;
    private readonly Counter<long> _fetchFailures;
    private readonly Counter<long> _fetchUnchanged;
    private readonly Counter<long> _fetchFallbacks;
    private readonly Counter<long> _cursorUpdates;
    private readonly Counter<long> _parseAttempts;
    private readonly Counter<long> _parseSuccess;
    private readonly Counter<long> _parseFailures;
    private readonly Counter<long> _mapSuccess;

    public AcscDiagnostics()
    {
        _meter = new Meter(MeterName, MeterVersion);
        _fetchAttempts = _meter.CreateCounter<long>("acsc.fetch.attempts", unit: "operations");
        _fetchSuccess = _meter.CreateCounter<long>("acsc.fetch.success", unit: "operations");
        _fetchFailures = _meter.CreateCounter<long>("acsc.fetch.failures", unit: "operations");
        _fetchUnchanged = _meter.CreateCounter<long>("acsc.fetch.unchanged", unit: "operations");
        _fetchFallbacks = _meter.CreateCounter<long>("acsc.fetch.fallbacks", unit: "operations");
        _cursorUpdates = _meter.CreateCounter<long>("acsc.cursor.published_updates", unit: "feeds");
        _parseAttempts = _meter.CreateCounter<long>("acsc.parse.attempts", unit: "documents");
        _parseSuccess = _meter.CreateCounter<long>("acsc.parse.success", unit: "documents");
        _parseFailures = _meter.CreateCounter<long>("acsc.parse.failures", unit: "documents");
        _mapSuccess = _meter.CreateCounter<long>("acsc.map.success", unit: "advisories");
    }

    public void FetchAttempt(string feed, string mode)
        => _fetchAttempts.Add(1, GetTags(feed, mode));

    public void FetchSuccess(string feed, string mode)
        => _fetchSuccess.Add(1, GetTags(feed, mode));

    public void FetchFailure(string feed, string mode)
        => _fetchFailures.Add(1, GetTags(feed, mode));

    public void FetchUnchanged(string feed, string mode)
        => _fetchUnchanged.Add(1, GetTags(feed, mode));

    public void FetchFallback(string feed, string mode, string reason)
        => _fetchFallbacks.Add(1, GetTags(feed, mode, new KeyValuePair<string, object?>("reason", reason)));

    public void CursorUpdated(string feed)
        => _cursorUpdates.Add(1, new KeyValuePair<string, object?>("feed", feed));

    public void ParseAttempt(string feed)
        => _parseAttempts.Add(1, new KeyValuePair<string, object?>("feed", feed));

    public void ParseSuccess(string feed)
        => _parseSuccess.Add(1, new KeyValuePair<string, object?>("feed", feed));

    public void ParseFailure(string feed, string reason)
        => _parseFailures.Add(1, new KeyValuePair<string, object?>[]
        {
            new("feed", feed),
            new("reason", reason),
        });

    public void MapSuccess(int advisoryCount)
    {
        if (advisoryCount <= 0)
        {
            return;
        }

        _mapSuccess.Add(advisoryCount);
    }

    private static KeyValuePair<string, object?>[] GetTags(string feed, string mode)
        => new[]
        {
            new KeyValuePair<string, object?>("feed", feed),
            new KeyValuePair<string, object?>("mode", mode),
        };

    private static KeyValuePair<string, object?>[] GetTags(string feed, string mode, KeyValuePair<string, object?> extra)
        => new[]
        {
            new KeyValuePair<string, object?>("feed", feed),
            new KeyValuePair<string, object?>("mode", mode),
            extra,
        };

    public void Dispose()
    {
        _meter.Dispose();
    }
}
