using System.Collections.Generic;
using System.Diagnostics.Metrics;

namespace StellaOps.Feedser.Source.Kev.Internal;

public sealed class KevDiagnostics : IDisposable
{
    public const string MeterName = "StellaOps.Feedser.Source.Kev";
    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> _parsedEntries;
    private readonly Counter<long> _parseFailures;
    private readonly Counter<long> _parseAnomalies;
    private readonly Counter<long> _mappedAdvisories;

    public KevDiagnostics()
    {
        _meter = new Meter(MeterName, MeterVersion);
        _fetchAttempts = _meter.CreateCounter<long>(
            name: "kev.fetch.attempts",
            unit: "operations",
            description: "Number of KEV fetch attempts performed.");
        _fetchSuccess = _meter.CreateCounter<long>(
            name: "kev.fetch.success",
            unit: "operations",
            description: "Number of KEV fetch attempts that produced new catalog content.");
        _fetchFailures = _meter.CreateCounter<long>(
            name: "kev.fetch.failures",
            unit: "operations",
            description: "Number of KEV fetch attempts that failed.");
        _fetchUnchanged = _meter.CreateCounter<long>(
            name: "kev.fetch.unchanged",
            unit: "operations",
            description: "Number of KEV fetch attempts returning HTTP 304 / unchanged catalog.");
        _parsedEntries = _meter.CreateCounter<long>(
            name: "kev.parse.entries",
            unit: "entries",
            description: "Number of KEV vulnerabilities parsed from the catalog.");
        _parseFailures = _meter.CreateCounter<long>(
            name: "kev.parse.failures",
            unit: "documents",
            description: "Number of KEV catalog parse operations that failed or were quarantined.");
        _parseAnomalies = _meter.CreateCounter<long>(
            name: "kev.parse.anomalies",
            unit: "entries",
            description: "Number of KEV entries skipped or flagged during parsing due to anomalies.");
        _mappedAdvisories = _meter.CreateCounter<long>(
            name: "kev.map.advisories",
            unit: "advisories",
            description: "Number of KEV advisories emitted during mapping.");
    }

    public void FetchAttempt() => _fetchAttempts.Add(1);

    public void FetchSuccess() => _fetchSuccess.Add(1);

    public void FetchFailure() => _fetchFailures.Add(1);

    public void FetchUnchanged() => _fetchUnchanged.Add(1);

    public void CatalogParsed(string? catalogVersion, int entryCount)
    {
        if (entryCount <= 0)
        {
            return;
        }

        _parsedEntries.Add(entryCount, new KeyValuePair<string, object?>("catalogVersion", catalogVersion ?? string.Empty));
    }

    public void ParseFailure(string reason, string? catalogVersion = null)
    {
        var tags = string.IsNullOrWhiteSpace(catalogVersion)
            ? new[] { new KeyValuePair<string, object?>("reason", reason) }
            : new[]
            {
                new KeyValuePair<string, object?>("reason", reason),
                new KeyValuePair<string, object?>("catalogVersion", catalogVersion)
            };

        _parseFailures.Add(1, tags);
    }

    public void RecordAnomaly(string reason, string? catalogVersion = null)
    {
        var tags = string.IsNullOrWhiteSpace(catalogVersion)
            ? new[] { new KeyValuePair<string, object?>("reason", reason) }
            : new[]
            {
                new KeyValuePair<string, object?>("reason", reason),
                new KeyValuePair<string, object?>("catalogVersion", catalogVersion)
            };

        _parseAnomalies.Add(1, tags);
    }

    public void AdvisoriesMapped(string? catalogVersion, int advisoryCount)
    {
        if (advisoryCount <= 0)
        {
            return;
        }

        _mappedAdvisories.Add(advisoryCount, new KeyValuePair<string, object?>("catalogVersion", catalogVersion ?? string.Empty));
    }

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