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

namespace StellaOps.Feedser.Source.Ics.Cisa.Internal;

public sealed class IcsCisaDiagnostics : IDisposable
{
    private const string MeterName = "StellaOps.Feedser.Source.Ics.Cisa";
    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> _fetchNotModified;
    private readonly Counter<long> _fetchFallbacks;
    private readonly Histogram<long> _fetchDocuments;

    private readonly Counter<long> _parseSuccess;
    private readonly Counter<long> _parseFailures;
    private readonly Histogram<long> _parseAdvisoryCount;
    private readonly Histogram<long> _parseAttachmentCount;
    private readonly Histogram<long> _parseDetailCount;

    private readonly Counter<long> _detailSuccess;
    private readonly Counter<long> _detailFailures;

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

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

        _fetchAttempts = _meter.CreateCounter<long>("icscisa.fetch.attempts", unit: "operations");
        _fetchSuccess = _meter.CreateCounter<long>("icscisa.fetch.success", unit: "operations");
        _fetchFailures = _meter.CreateCounter<long>("icscisa.fetch.failures", unit: "operations");
        _fetchNotModified = _meter.CreateCounter<long>("icscisa.fetch.not_modified", unit: "operations");
        _fetchFallbacks = _meter.CreateCounter<long>("icscisa.fetch.fallbacks", unit: "operations");
        _fetchDocuments = _meter.CreateHistogram<long>("icscisa.fetch.documents", unit: "documents");

        _parseSuccess = _meter.CreateCounter<long>("icscisa.parse.success", unit: "documents");
        _parseFailures = _meter.CreateCounter<long>("icscisa.parse.failures", unit: "documents");
        _parseAdvisoryCount = _meter.CreateHistogram<long>("icscisa.parse.advisories", unit: "advisories");
        _parseAttachmentCount = _meter.CreateHistogram<long>("icscisa.parse.attachments", unit: "attachments");
        _parseDetailCount = _meter.CreateHistogram<long>("icscisa.parse.detail_fetches", unit: "fetches");

        _detailSuccess = _meter.CreateCounter<long>("icscisa.detail.success", unit: "operations");
        _detailFailures = _meter.CreateCounter<long>("icscisa.detail.failures", unit: "operations");

        _mapSuccess = _meter.CreateCounter<long>("icscisa.map.success", unit: "advisories");
        _mapFailures = _meter.CreateCounter<long>("icscisa.map.failures", unit: "advisories");
        _mapReferenceCount = _meter.CreateHistogram<long>("icscisa.map.references", unit: "references");
        _mapPackageCount = _meter.CreateHistogram<long>("icscisa.map.packages", unit: "packages");
        _mapAliasCount = _meter.CreateHistogram<long>("icscisa.map.aliases", unit: "aliases");
    }

    public void FetchAttempt(string topicId)
    {
        _fetchAttempts.Add(1, BuildTopicTags(topicId));
    }

    public void FetchSuccess(string topicId, int documentsAdded)
    {
        var tags = BuildTopicTags(topicId);
        _fetchSuccess.Add(1, tags);
        if (documentsAdded > 0)
        {
            _fetchDocuments.Record(documentsAdded, tags);
        }
    }

    public void FetchNotModified(string topicId)
    {
        _fetchNotModified.Add(1, BuildTopicTags(topicId));
    }

    public void FetchFallback(string topicId)
    {
        _fetchFallbacks.Add(1, BuildTopicTags(topicId));
    }

    public void FetchFailure(string topicId)
    {
        _fetchFailures.Add(1, BuildTopicTags(topicId));
    }

    public void ParseSuccess(string topicId, int advisoryCount, int attachmentCount, int detailFetchCount)
    {
        var tags = BuildTopicTags(topicId);
        _parseSuccess.Add(1, tags);
        if (advisoryCount >= 0)
        {
            _parseAdvisoryCount.Record(advisoryCount, tags);
        }

        if (attachmentCount >= 0)
        {
            _parseAttachmentCount.Record(attachmentCount, tags);
        }

        if (detailFetchCount >= 0)
        {
            _parseDetailCount.Record(detailFetchCount, tags);
        }
    }

    public void ParseFailure(string topicId)
    {
        _parseFailures.Add(1, BuildTopicTags(topicId));
    }

    public void DetailFetchSuccess(string advisoryId)
    {
        _detailSuccess.Add(1, BuildAdvisoryTags(advisoryId));
    }

    public void DetailFetchFailure(string advisoryId)
    {
        _detailFailures.Add(1, BuildAdvisoryTags(advisoryId));
    }

    public void MapSuccess(string advisoryId, int referenceCount, int packageCount, int aliasCount)
    {
        var tags = BuildAdvisoryTags(advisoryId);
        _mapSuccess.Add(1, tags);
        if (referenceCount >= 0)
        {
            _mapReferenceCount.Record(referenceCount, tags);
        }

        if (packageCount >= 0)
        {
            _mapPackageCount.Record(packageCount, tags);
        }

        if (aliasCount >= 0)
        {
            _mapAliasCount.Record(aliasCount, tags);
        }
    }

    public void MapFailure(string advisoryId)
    {
        _mapFailures.Add(1, BuildAdvisoryTags(advisoryId));
    }

    private static KeyValuePair<string, object?>[] BuildTopicTags(string? topicId)
        => new[]
        {
            new KeyValuePair<string, object?>("feedser.source", IcsCisaConnectorPlugin.SourceName),
            new KeyValuePair<string, object?>("icscisa.topic", string.IsNullOrWhiteSpace(topicId) ? "unknown" : topicId)
        };

    private static KeyValuePair<string, object?>[] BuildAdvisoryTags(string? advisoryId)
        => new[]
        {
            new KeyValuePair<string, object?>("feedser.source", IcsCisaConnectorPlugin.SourceName),
            new KeyValuePair<string, object?>("icscisa.advisory", string.IsNullOrWhiteSpace(advisoryId) ? "unknown" : advisoryId)
        };

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