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

namespace StellaOps.Feedser.Source.Vndr.Msrc.Internal;

public sealed class MsrcDiagnostics : IDisposable
{
    private const string MeterName = "StellaOps.Feedser.Source.Vndr.Msrc";
    private const string MeterVersion = "1.0.0";

    private readonly Meter _meter;
    private readonly Counter<long> _summaryFetchAttempts;
    private readonly Counter<long> _summaryFetchSuccess;
    private readonly Counter<long> _summaryFetchFailures;
    private readonly Histogram<long> _summaryItemCount;
    private readonly Histogram<double> _summaryWindowHours;
    private readonly Counter<long> _detailFetchAttempts;
    private readonly Counter<long> _detailFetchSuccess;
    private readonly Counter<long> _detailFetchNotModified;
    private readonly Counter<long> _detailFetchFailures;
    private readonly Histogram<long> _detailEnqueued;
    private readonly Counter<long> _parseSuccess;
    private readonly Counter<long> _parseFailures;
    private readonly Histogram<long> _parseProductCount;
    private readonly Histogram<long> _parseKbCount;
    private readonly Counter<long> _mapSuccess;
    private readonly Counter<long> _mapFailures;
    private readonly Histogram<long> _mapAliasCount;
    private readonly Histogram<long> _mapAffectedCount;

    public MsrcDiagnostics()
    {
        _meter = new Meter(MeterName, MeterVersion);
        _summaryFetchAttempts = _meter.CreateCounter<long>("msrc.summary.fetch.attempts", "operations");
        _summaryFetchSuccess = _meter.CreateCounter<long>("msrc.summary.fetch.success", "operations");
        _summaryFetchFailures = _meter.CreateCounter<long>("msrc.summary.fetch.failures", "operations");
        _summaryItemCount = _meter.CreateHistogram<long>("msrc.summary.items.count", "items");
        _summaryWindowHours = _meter.CreateHistogram<double>("msrc.summary.window.hours", "hours");
        _detailFetchAttempts = _meter.CreateCounter<long>("msrc.detail.fetch.attempts", "operations");
        _detailFetchSuccess = _meter.CreateCounter<long>("msrc.detail.fetch.success", "operations");
        _detailFetchNotModified = _meter.CreateCounter<long>("msrc.detail.fetch.not_modified", "operations");
        _detailFetchFailures = _meter.CreateCounter<long>("msrc.detail.fetch.failures", "operations");
        _detailEnqueued = _meter.CreateHistogram<long>("msrc.detail.enqueued.count", "documents");
        _parseSuccess = _meter.CreateCounter<long>("msrc.parse.success", "documents");
        _parseFailures = _meter.CreateCounter<long>("msrc.parse.failures", "documents");
        _parseProductCount = _meter.CreateHistogram<long>("msrc.parse.products.count", "products");
        _parseKbCount = _meter.CreateHistogram<long>("msrc.parse.kb.count", "kb");
        _mapSuccess = _meter.CreateCounter<long>("msrc.map.success", "advisories");
        _mapFailures = _meter.CreateCounter<long>("msrc.map.failures", "advisories");
        _mapAliasCount = _meter.CreateHistogram<long>("msrc.map.aliases.count", "aliases");
        _mapAffectedCount = _meter.CreateHistogram<long>("msrc.map.affected.count", "packages");
    }

    public void SummaryFetchAttempt() => _summaryFetchAttempts.Add(1);

    public void SummaryFetchSuccess(int count, double? windowHours)
    {
        _summaryFetchSuccess.Add(1);
        if (count >= 0)
        {
            _summaryItemCount.Record(count);
        }

        if (windowHours is { } value && value >= 0)
        {
            _summaryWindowHours.Record(value);
        }
    }

    public void SummaryFetchFailure(string reason)
        => _summaryFetchFailures.Add(1, ReasonTag(reason));

    public void DetailFetchAttempt() => _detailFetchAttempts.Add(1);

    public void DetailFetchSuccess() => _detailFetchSuccess.Add(1);

    public void DetailFetchNotModified() => _detailFetchNotModified.Add(1);

    public void DetailFetchFailure(string reason)
        => _detailFetchFailures.Add(1, ReasonTag(reason));

    public void DetailEnqueued(int count)
    {
        if (count >= 0)
        {
            _detailEnqueued.Record(count);
        }
    }

    public void ParseSuccess(int productCount, int kbCount)
    {
        _parseSuccess.Add(1);
        if (productCount >= 0)
        {
            _parseProductCount.Record(productCount);
        }

        if (kbCount >= 0)
        {
            _parseKbCount.Record(kbCount);
        }
    }

    public void ParseFailure(string reason)
        => _parseFailures.Add(1, ReasonTag(reason));

    public void MapSuccess(int aliasCount, int packageCount)
    {
        _mapSuccess.Add(1);
        if (aliasCount >= 0)
        {
            _mapAliasCount.Record(aliasCount);
        }

        if (packageCount >= 0)
        {
            _mapAffectedCount.Record(packageCount);
        }
    }

    public void MapFailure(string reason)
        => _mapFailures.Add(1, ReasonTag(reason));

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

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