using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using StellaOps.Feedser.Source.Common.Cursors;

namespace StellaOps.Feedser.Source.CertCc.Internal;

/// <summary>
/// Emits CERT/CC-specific telemetry for summary planning and fetch activity.
/// </summary>
public sealed class CertCcDiagnostics : IDisposable
{
    private const string MeterName = "StellaOps.Feedser.Source.CertCc";
    private const string MeterVersion = "1.0.0";

    private readonly Meter _meter;
    private readonly Counter<long> _planWindows;
    private readonly Counter<long> _planRequests;
    private readonly Histogram<double> _planWindowDays;
    private readonly Counter<long> _summaryFetchAttempts;
    private readonly Counter<long> _summaryFetchSuccess;
    private readonly Counter<long> _summaryFetchUnchanged;
    private readonly Counter<long> _summaryFetchFailures;
    private readonly Counter<long> _detailFetchAttempts;
    private readonly Counter<long> _detailFetchSuccess;
    private readonly Counter<long> _detailFetchUnchanged;
    private readonly Counter<long> _detailFetchMissing;
    private readonly Counter<long> _detailFetchFailures;
    private readonly Counter<long> _parseSuccess;
    private readonly Counter<long> _parseFailures;
    private readonly Histogram<long> _parseVendorCount;
    private readonly Histogram<long> _parseStatusCount;
    private readonly Histogram<long> _parseVulnerabilityCount;
    private readonly Counter<long> _mapSuccess;
    private readonly Counter<long> _mapFailures;
    private readonly Histogram<long> _mapAffectedPackageCount;
    private readonly Histogram<long> _mapNormalizedVersionCount;

    public CertCcDiagnostics()
    {
        _meter = new Meter(MeterName, MeterVersion);
        _planWindows = _meter.CreateCounter<long>(
            name: "certcc.plan.windows",
            unit: "windows",
            description: "Number of summary planning windows evaluated.");
        _planRequests = _meter.CreateCounter<long>(
            name: "certcc.plan.requests",
            unit: "requests",
            description: "Total CERT/CC summary endpoints queued by the planner.");
        _planWindowDays = _meter.CreateHistogram<double>(
            name: "certcc.plan.window_days",
            unit: "day",
            description: "Duration of each planning window in days.");
        _summaryFetchAttempts = _meter.CreateCounter<long>(
            name: "certcc.summary.fetch.attempts",
            unit: "operations",
            description: "Number of VINCE summary fetch attempts.");
        _summaryFetchSuccess = _meter.CreateCounter<long>(
            name: "certcc.summary.fetch.success",
            unit: "operations",
            description: "Number of VINCE summary fetches persisted to storage.");
        _summaryFetchUnchanged = _meter.CreateCounter<long>(
            name: "certcc.summary.fetch.not_modified",
            unit: "operations",
            description: "Number of VINCE summary fetches returning HTTP 304.");
        _summaryFetchFailures = _meter.CreateCounter<long>(
            name: "certcc.summary.fetch.failures",
            unit: "operations",
            description: "Number of VINCE summary fetches that failed after retries.");
        _detailFetchAttempts = _meter.CreateCounter<long>(
            name: "certcc.detail.fetch.attempts",
            unit: "operations",
            description: "Number of VINCE detail fetch attempts.");
        _detailFetchSuccess = _meter.CreateCounter<long>(
            name: "certcc.detail.fetch.success",
            unit: "operations",
            description: "Number of VINCE detail fetches that returned payloads.");
        _detailFetchUnchanged = _meter.CreateCounter<long>(
            name: "certcc.detail.fetch.unchanged",
            unit: "operations",
            description: "Number of VINCE detail fetches returning HTTP 304.");
        _detailFetchMissing = _meter.CreateCounter<long>(
            name: "certcc.detail.fetch.missing",
            unit: "operations",
            description: "Number of optional VINCE detail endpoints missing but tolerated.");
        _detailFetchFailures = _meter.CreateCounter<long>(
            name: "certcc.detail.fetch.failures",
            unit: "operations",
            description: "Number of VINCE detail fetches that failed after retries.");
        _parseSuccess = _meter.CreateCounter<long>(
            name: "certcc.parse.success",
            unit: "documents",
            description: "Number of VINCE note bundles parsed into DTOs.");
        _parseFailures = _meter.CreateCounter<long>(
            name: "certcc.parse.failures",
            unit: "documents",
            description: "Number of VINCE note bundles that failed to parse.");
        _parseVendorCount = _meter.CreateHistogram<long>(
            name: "certcc.parse.vendors.count",
            unit: "vendors",
            description: "Distribution of vendor statements per VINCE note.");
        _parseStatusCount = _meter.CreateHistogram<long>(
            name: "certcc.parse.statuses.count",
            unit: "entries",
            description: "Distribution of vendor status entries per VINCE note.");
        _parseVulnerabilityCount = _meter.CreateHistogram<long>(
            name: "certcc.parse.vulnerabilities.count",
            unit: "entries",
            description: "Distribution of vulnerability records per VINCE note.");
        _mapSuccess = _meter.CreateCounter<long>(
            name: "certcc.map.success",
            unit: "advisories",
            description: "Number of canonical advisories emitted by the CERT/CC mapper.");
        _mapFailures = _meter.CreateCounter<long>(
            name: "certcc.map.failures",
            unit: "advisories",
            description: "Number of CERT/CC advisory mapping attempts that failed.");
        _mapAffectedPackageCount = _meter.CreateHistogram<long>(
            name: "certcc.map.affected.count",
            unit: "packages",
            description: "Distribution of affected packages emitted per CERT/CC advisory.");
        _mapNormalizedVersionCount = _meter.CreateHistogram<long>(
            name: "certcc.map.normalized_versions.count",
            unit: "rules",
            description: "Distribution of normalized version rules emitted per CERT/CC advisory.");
    }

    public void PlanEvaluated(TimeWindow window, int requestCount)
    {
        _planWindows.Add(1);

        if (requestCount > 0)
        {
            _planRequests.Add(requestCount);
        }

        var duration = window.Duration;
        if (duration > TimeSpan.Zero)
        {
            _planWindowDays.Record(duration.TotalDays);
        }
    }

    public void SummaryFetchAttempt(CertCcSummaryScope scope)
        => _summaryFetchAttempts.Add(1, ScopeTag(scope));

    public void SummaryFetchSuccess(CertCcSummaryScope scope)
        => _summaryFetchSuccess.Add(1, ScopeTag(scope));

    public void SummaryFetchUnchanged(CertCcSummaryScope scope)
        => _summaryFetchUnchanged.Add(1, ScopeTag(scope));

    public void SummaryFetchFailure(CertCcSummaryScope scope)
        => _summaryFetchFailures.Add(1, ScopeTag(scope));

    public void DetailFetchAttempt(string endpoint)
        => _detailFetchAttempts.Add(1, EndpointTag(endpoint));

    public void DetailFetchSuccess(string endpoint)
        => _detailFetchSuccess.Add(1, EndpointTag(endpoint));

    public void DetailFetchUnchanged(string endpoint)
        => _detailFetchUnchanged.Add(1, EndpointTag(endpoint));

    public void DetailFetchMissing(string endpoint)
        => _detailFetchMissing.Add(1, EndpointTag(endpoint));

    public void DetailFetchFailure(string endpoint)
        => _detailFetchFailures.Add(1, EndpointTag(endpoint));

    public void ParseSuccess(int vendorCount, int statusCount, int vulnerabilityCount)
    {
        _parseSuccess.Add(1);
        if (vendorCount >= 0)
        {
            _parseVendorCount.Record(vendorCount);
        }
        if (statusCount >= 0)
        {
            _parseStatusCount.Record(statusCount);
        }
        if (vulnerabilityCount >= 0)
        {
            _parseVulnerabilityCount.Record(vulnerabilityCount);
        }
    }

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

    public void MapSuccess(int affectedPackageCount, int normalizedVersionCount)
    {
        _mapSuccess.Add(1);
        if (affectedPackageCount >= 0)
        {
            _mapAffectedPackageCount.Record(affectedPackageCount);
        }
        if (normalizedVersionCount >= 0)
        {
            _mapNormalizedVersionCount.Record(normalizedVersionCount);
        }
    }

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

    private static KeyValuePair<string, object?> ScopeTag(CertCcSummaryScope scope)
        => new("scope", scope.ToString().ToLowerInvariant());

    private static KeyValuePair<string, object?> EndpointTag(string endpoint)
        => new("endpoint", string.IsNullOrWhiteSpace(endpoint) ? "note" : endpoint.ToLowerInvariant());

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