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

namespace StellaOps.Feedser.Source.Ru.Nkcki.Internal;

/// <summary>
/// Emits telemetry counters for the NKCKI connector lifecycle.
/// </summary>
public sealed class RuNkckiDiagnostics : IDisposable
{
    private const string MeterName = "StellaOps.Feedser.Source.Ru.Nkcki";
    private const string MeterVersion = "1.0.0";

    private readonly Meter _meter;
    private readonly Counter<long> _listingFetchAttempts;
    private readonly Counter<long> _listingFetchSuccess;
    private readonly Counter<long> _listingFetchFailures;
    private readonly Histogram<long> _listingPagesVisited;
    private readonly Histogram<long> _listingAttachmentsDiscovered;
    private readonly Histogram<long> _listingAttachmentsNew;
    private readonly Counter<long> _bulletinFetchSuccess;
    private readonly Counter<long> _bulletinFetchCached;
    private readonly Counter<long> _bulletinFetchFailures;
    private readonly Histogram<long> _entriesProcessed;

    public RuNkckiDiagnostics()
    {
        _meter = new Meter(MeterName, MeterVersion);
        _listingFetchAttempts = _meter.CreateCounter<long>(
            "nkcki.listing.fetch.attempts",
            unit: "operations",
            description: "Number of listing fetch attempts.");
        _listingFetchSuccess = _meter.CreateCounter<long>(
            "nkcki.listing.fetch.success",
            unit: "operations",
            description: "Number of successful listing fetches.");
        _listingFetchFailures = _meter.CreateCounter<long>(
            "nkcki.listing.fetch.failures",
            unit: "operations",
            description: "Number of listing fetch failures.");
        _listingPagesVisited = _meter.CreateHistogram<long>(
            "nkcki.listing.pages.visited",
            unit: "pages",
            description: "Listing pages visited per fetch cycle.");
        _listingAttachmentsDiscovered = _meter.CreateHistogram<long>(
            "nkcki.listing.attachments.discovered",
            unit: "attachments",
            description: "Attachments discovered across listing pages.");
        _listingAttachmentsNew = _meter.CreateHistogram<long>(
            "nkcki.listing.attachments.new",
            unit: "attachments",
            description: "New bulletin attachments enqueued per fetch cycle.");
        _bulletinFetchSuccess = _meter.CreateCounter<long>(
            "nkcki.bulletin.fetch.success",
            unit: "operations",
            description: "Number of bulletin downloads that succeeded.");
        _bulletinFetchCached = _meter.CreateCounter<long>(
            "nkcki.bulletin.fetch.cached",
            unit: "operations",
            description: "Number of bulletins served from cache.");
        _bulletinFetchFailures = _meter.CreateCounter<long>(
            "nkcki.bulletin.fetch.failures",
            unit: "operations",
            description: "Number of bulletin download failures.");
        _entriesProcessed = _meter.CreateHistogram<long>(
            "nkcki.entries.processed",
            unit: "entries",
            description: "Number of vulnerability entries processed per bulletin.");
    }

    public void ListingFetchAttempt() => _listingFetchAttempts.Add(1);

    public void ListingFetchSuccess(int pagesVisited, int attachmentsDiscovered, int attachmentsNew)
    {
        _listingFetchSuccess.Add(1);
        if (pagesVisited >= 0)
        {
            _listingPagesVisited.Record(pagesVisited);
        }

        if (attachmentsDiscovered >= 0)
        {
            _listingAttachmentsDiscovered.Record(attachmentsDiscovered);
        }

        if (attachmentsNew >= 0)
        {
            _listingAttachmentsNew.Record(attachmentsNew);
        }
    }

    public void ListingFetchFailure(string reason)
        => _listingFetchFailures.Add(1, ReasonTag(reason));

    public void BulletinFetchSuccess() => _bulletinFetchSuccess.Add(1);

    public void BulletinFetchCached() => _bulletinFetchCached.Add(1);

    public void BulletinFetchFailure(string reason)
        => _bulletinFetchFailures.Add(1, ReasonTag(reason));

    public void EntriesProcessed(int count)
    {
        if (count >= 0)
        {
            _entriesProcessed.Record(count);
        }
    }

    private static KeyValuePair<string, object?> ReasonTag(string reason)
        => new("reason", string.IsNullOrWhiteSpace(reason) ? "unknown" : reason.ToLowerInvariant());

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