using System;
using System.Diagnostics.Metrics;
using StellaOps.Feedser.Models;

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

/// <summary>
/// Emits RU-BDU specific OpenTelemetry metrics for fetch/parse/map stages.
/// </summary>
public sealed class RuBduDiagnostics : IDisposable
{
    private const string MeterName = "StellaOps.Feedser.Source.Ru.Bdu";
    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> _fetchCacheFallbacks;
    private readonly Histogram<long> _fetchDocumentAdds;

    private readonly Counter<long> _parseSuccess;
    private readonly Counter<long> _parseFailures;
    private readonly Histogram<long> _parseSoftwareCount;
    private readonly Histogram<long> _parseIdentifierCount;
    private readonly Histogram<long> _parseSourceCount;

    private readonly Counter<long> _mapSuccess;
    private readonly Counter<long> _mapFailures;
    private readonly Histogram<long> _mapPackageCount;
    private readonly Histogram<long> _mapAliasCount;

    public RuBduDiagnostics()
    {
        _meter = new Meter(MeterName, MeterVersion);

        _fetchAttempts = _meter.CreateCounter<long>(
            name: "ru.bdu.fetch.attempts",
            unit: "operations",
            description: "Number of RU-BDU archive fetch attempts.");
        _fetchSuccess = _meter.CreateCounter<long>(
            name: "ru.bdu.fetch.success",
            unit: "operations",
            description: "Number of RU-BDU archive fetches processed successfully.");
        _fetchFailures = _meter.CreateCounter<long>(
            name: "ru.bdu.fetch.failures",
            unit: "operations",
            description: "Number of RU-BDU archive fetches that failed.");
        _fetchUnchanged = _meter.CreateCounter<long>(
            name: "ru.bdu.fetch.not_modified",
            unit: "operations",
            description: "Number of RU-BDU archive fetches returning HTTP 304.");
        _fetchCacheFallbacks = _meter.CreateCounter<long>(
            name: "ru.bdu.fetch.cache_fallbacks",
            unit: "operations",
            description: "Number of RU-BDU fetches that fell back to the cached archive.");
        _fetchDocumentAdds = _meter.CreateHistogram<long>(
            name: "ru.bdu.fetch.documents",
            unit: "documents",
            description: "Distribution of new documents written per RU-BDU fetch.");

        _parseSuccess = _meter.CreateCounter<long>(
            name: "ru.bdu.parse.success",
            unit: "documents",
            description: "Number of RU-BDU documents parsed into DTOs.");
        _parseFailures = _meter.CreateCounter<long>(
            name: "ru.bdu.parse.failures",
            unit: "documents",
            description: "Number of RU-BDU documents that failed parsing.");
        _parseSoftwareCount = _meter.CreateHistogram<long>(
            name: "ru.bdu.parse.software.count",
            unit: "entries",
            description: "Distribution of vulnerable software entries per RU-BDU DTO.");
        _parseIdentifierCount = _meter.CreateHistogram<long>(
            name: "ru.bdu.parse.identifiers.count",
            unit: "entries",
            description: "Distribution of external identifiers per RU-BDU DTO.");
        _parseSourceCount = _meter.CreateHistogram<long>(
            name: "ru.bdu.parse.sources.count",
            unit: "entries",
            description: "Distribution of source references per RU-BDU DTO.");

        _mapSuccess = _meter.CreateCounter<long>(
            name: "ru.bdu.map.success",
            unit: "advisories",
            description: "Number of canonical advisories emitted by the RU-BDU mapper.");
        _mapFailures = _meter.CreateCounter<long>(
            name: "ru.bdu.map.failures",
            unit: "advisories",
            description: "Number of RU-BDU advisory mapping attempts that failed.");
        _mapPackageCount = _meter.CreateHistogram<long>(
            name: "ru.bdu.map.packages.count",
            unit: "packages",
            description: "Distribution of affected packages per RU-BDU advisory.");
        _mapAliasCount = _meter.CreateHistogram<long>(
            name: "ru.bdu.map.aliases.count",
            unit: "aliases",
            description: "Distribution of aliases per RU-BDU advisory.");
    }

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

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

    public void FetchCacheFallback() => _fetchCacheFallbacks.Add(1);

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

    public void FetchSuccess(int addedCount, bool usedCache)
    {
        _ = usedCache;
        _fetchSuccess.Add(1);
        if (addedCount > 0)
        {
            _fetchDocumentAdds.Record(addedCount);
        }
    }

    public void ParseSuccess(int softwareCount, int identifierCount, int sourceCount)
    {
        _parseSuccess.Add(1);
        _parseSoftwareCount.Record(Math.Max(softwareCount, 0));
        _parseIdentifierCount.Record(Math.Max(identifierCount, 0));
        _parseSourceCount.Record(Math.Max(sourceCount, 0));
    }

    public void ParseFailure() => _parseFailures.Add(1);

    public void MapSuccess(Advisory advisory)
    {
        _mapSuccess.Add(1);
        _mapPackageCount.Record(advisory.AffectedPackages.Length);
        _mapAliasCount.Record(advisory.Aliases.Length);
    }

    public void MapFailure() => _mapFailures.Add(1);

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