using System.Diagnostics.Metrics;

namespace StellaOps.Feedser.Source.Kisa.Internal;

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

    private readonly Meter _meter;
    private readonly Counter<long> _feedAttempts;
    private readonly Counter<long> _feedSuccess;
    private readonly Counter<long> _feedFailures;
    private readonly Counter<long> _feedItems;
    private readonly Counter<long> _detailAttempts;
    private readonly Counter<long> _detailSuccess;
    private readonly Counter<long> _detailUnchanged;
    private readonly Counter<long> _detailFailures;
    private readonly Counter<long> _parseAttempts;
    private readonly Counter<long> _parseSuccess;
    private readonly Counter<long> _parseFailures;
    private readonly Counter<long> _mapSuccess;
    private readonly Counter<long> _mapFailures;
    private readonly Counter<long> _cursorUpdates;

    public KisaDiagnostics()
    {
        _meter = new Meter(MeterName, MeterVersion);
        _feedAttempts = _meter.CreateCounter<long>(
            name: "kisa.feed.attempts",
            unit: "operations",
            description: "Number of RSS fetch attempts performed for the KISA connector.");
        _feedSuccess = _meter.CreateCounter<long>(
            name: "kisa.feed.success",
            unit: "operations",
            description: "Number of RSS fetch attempts that completed successfully.");
        _feedFailures = _meter.CreateCounter<long>(
            name: "kisa.feed.failures",
            unit: "operations",
            description: "Number of RSS fetch attempts that failed.");
        _feedItems = _meter.CreateCounter<long>(
            name: "kisa.feed.items",
            unit: "items",
            description: "Number of feed items returned by successful RSS fetches.");
        _detailAttempts = _meter.CreateCounter<long>(
            name: "kisa.detail.attempts",
            unit: "documents",
            description: "Number of advisory detail fetch attempts.");
        _detailSuccess = _meter.CreateCounter<long>(
            name: "kisa.detail.success",
            unit: "documents",
            description: "Number of advisory detail documents fetched successfully.");
        _detailUnchanged = _meter.CreateCounter<long>(
            name: "kisa.detail.unchanged",
            unit: "documents",
            description: "Number of advisory detail fetches that returned HTTP 304 (no change).");
        _detailFailures = _meter.CreateCounter<long>(
            name: "kisa.detail.failures",
            unit: "documents",
            description: "Number of advisory detail fetch attempts that failed.");
        _parseAttempts = _meter.CreateCounter<long>(
            name: "kisa.parse.attempts",
            unit: "documents",
            description: "Number of advisory documents queued for parsing.");
        _parseSuccess = _meter.CreateCounter<long>(
            name: "kisa.parse.success",
            unit: "documents",
            description: "Number of advisory documents parsed successfully into DTOs.");
        _parseFailures = _meter.CreateCounter<long>(
            name: "kisa.parse.failures",
            unit: "documents",
            description: "Number of advisory documents that failed parsing.");
        _mapSuccess = _meter.CreateCounter<long>(
            name: "kisa.map.success",
            unit: "advisories",
            description: "Number of canonical advisories produced by the mapper.");
        _mapFailures = _meter.CreateCounter<long>(
            name: "kisa.map.failures",
            unit: "advisories",
            description: "Number of advisories that failed to map.");
        _cursorUpdates = _meter.CreateCounter<long>(
            name: "kisa.cursor.updates",
            unit: "updates",
            description: "Number of times the published cursor advanced.");
    }

    public void FeedAttempt() => _feedAttempts.Add(1);

    public void FeedSuccess(int itemCount)
    {
        _feedSuccess.Add(1);
        if (itemCount > 0)
        {
            _feedItems.Add(itemCount);
        }
    }

    public void FeedFailure(string reason)
        => _feedFailures.Add(1, GetReasonTags(reason));

    public void DetailAttempt(string? category)
        => _detailAttempts.Add(1, GetCategoryTags(category));

    public void DetailSuccess(string? category)
        => _detailSuccess.Add(1, GetCategoryTags(category));

    public void DetailUnchanged(string? category)
        => _detailUnchanged.Add(1, GetCategoryTags(category));

    public void DetailFailure(string? category, string reason)
        => _detailFailures.Add(1, GetCategoryReasonTags(category, reason));

    public void ParseAttempt(string? category)
        => _parseAttempts.Add(1, GetCategoryTags(category));

    public void ParseSuccess(string? category)
        => _parseSuccess.Add(1, GetCategoryTags(category));

    public void ParseFailure(string? category, string reason)
        => _parseFailures.Add(1, GetCategoryReasonTags(category, reason));

    public void MapSuccess(string? severity)
        => _mapSuccess.Add(1, GetSeverityTags(severity));

    public void MapFailure(string? severity, string reason)
        => _mapFailures.Add(1, GetSeverityReasonTags(severity, reason));

    public void CursorAdvanced()
        => _cursorUpdates.Add(1);

    public Meter Meter => _meter;

    public void Dispose() => _meter.Dispose();

    private static KeyValuePair<string, object?>[] GetCategoryTags(string? category)
        => new[]
        {
            new KeyValuePair<string, object?>("category", Normalize(category))
        };

    private static KeyValuePair<string, object?>[] GetCategoryReasonTags(string? category, string reason)
        => new[]
        {
            new KeyValuePair<string, object?>("category", Normalize(category)),
            new KeyValuePair<string, object?>("reason", Normalize(reason)),
        };

    private static KeyValuePair<string, object?>[] GetSeverityTags(string? severity)
        => new[]
        {
            new KeyValuePair<string, object?>("severity", Normalize(severity)),
        };

    private static KeyValuePair<string, object?>[] GetSeverityReasonTags(string? severity, string reason)
        => new[]
        {
            new KeyValuePair<string, object?>("severity", Normalize(severity)),
            new KeyValuePair<string, object?>("reason", Normalize(reason)),
        };

    private static KeyValuePair<string, object?>[] GetReasonTags(string reason)
        => new[]
        {
            new KeyValuePair<string, object?>("reason", Normalize(reason)),
        };

    private static string Normalize(string? value)
        => string.IsNullOrWhiteSpace(value) ? "unknown" : value!;
}
