using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.Metrics;
using StellaOps.Feedser.Models;
using Xunit;

namespace StellaOps.Feedser.Models.Tests;

public sealed class OsvGhsaParityDiagnosticsTests
{
    [Fact]
    public void RecordReport_EmitsTotalAndIssues()
    {
        var issues = ImmutableArray.Create(
            new OsvGhsaParityIssue(
                GhsaId: "GHSA-AAA",
                IssueKind: "missing_osv",
                Detail: "",
                FieldMask: ImmutableArray.Create(ProvenanceFieldMasks.AffectedPackages)),
            new OsvGhsaParityIssue(
                GhsaId: "GHSA-BBB",
                IssueKind: "severity_mismatch",
                Detail: "",
                FieldMask: ImmutableArray<string>.Empty));
        var report = new OsvGhsaParityReport(2, issues);

        var measurements = new List<(string Instrument, long Value, IReadOnlyDictionary<string, object?> Tags)>();
        using var listener = CreateListener(measurements);

        OsvGhsaParityDiagnostics.RecordReport(report, "QA");

        listener.Dispose();

        Assert.Equal(3, measurements.Count);

        var total = Assert.Single(measurements, m => m.Instrument == "feedser.osv_ghsa.total");
        Assert.Equal(2, total.Value);
        Assert.Equal("qa", total.Tags["dataset"]);

        var missing = Assert.Single(measurements, m => m.Tags.TryGetValue("issueKind", out var kind) && string.Equals((string)kind!, "missing_osv", StringComparison.Ordinal));
        Assert.Equal("affectedpackages[]", missing.Tags["fieldMask"]);

        var severity = Assert.Single(measurements, m => m.Tags.TryGetValue("issueKind", out var kind) && string.Equals((string)kind!, "severity_mismatch", StringComparison.Ordinal));
        Assert.Equal("none", severity.Tags["fieldMask"]);
    }

    [Fact]
    public void RecordReport_NoIssues_OnlyEmitsTotal()
    {
        var report = new OsvGhsaParityReport(0, ImmutableArray<OsvGhsaParityIssue>.Empty);
        var measurements = new List<(string Instrument, long Value, IReadOnlyDictionary<string, object?> Tags)>();
        using var listener = CreateListener(measurements);

        OsvGhsaParityDiagnostics.RecordReport(report, "");

        listener.Dispose();
        Assert.Empty(measurements);
    }

    private static MeterListener CreateListener(List<(string Instrument, long Value, IReadOnlyDictionary<string, object?> Tags)> measurements)
    {
        var listener = new MeterListener
        {
            InstrumentPublished = (instrument, l) =>
            {
                if (instrument.Meter.Name.StartsWith("StellaOps.Feedser.Models.OsvGhsaParity", StringComparison.Ordinal))
                {
                    l.EnableMeasurementEvents(instrument);
                }
            }
        };

        listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
        {
            var dict = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
            foreach (var tag in tags)
            {
                dict[tag.Key] = tag.Value;
            }

            measurements.Add((instrument.Name, measurement, dict));
        });

        listener.Start();
        return listener;
    }
}
