using System.Diagnostics;
using System.Diagnostics.Metrics;
using Jiaowu.Api.Domain.System;

namespace Jiaowu.Api.Infrastructure.BackgroundJobs;

public sealed class BackgroundJobTelemetry : IDisposable
{
    public const string MeterName = "Jiaowu.BackgroundJobs";

    private readonly Meter _meter = new(MeterName, "1.0.0");
    private readonly Counter<long> _published;
    private readonly Counter<long> _processed;
    private readonly Histogram<double> _publishDuration;
    private readonly Histogram<double> _processingDuration;
    private readonly Counter<long> _cleaned;

    public BackgroundJobTelemetry()
    {
        _published = _meter.CreateCounter<long>(
            "jiaowu.background_jobs.published",
            unit: "{message}");
        _processed = _meter.CreateCounter<long>(
            "jiaowu.background_jobs.processed",
            unit: "{job}");
        _publishDuration = _meter.CreateHistogram<double>(
            "jiaowu.background_jobs.publish.duration",
            unit: "ms");
        _processingDuration = _meter.CreateHistogram<double>(
            "jiaowu.background_jobs.processing.duration",
            unit: "ms");
        _cleaned = _meter.CreateCounter<long>(
            "jiaowu.background_jobs.outbox.cleaned",
            unit: "{message}");
    }

    public void RecordPublish(
        BackgroundJobKind kind,
        bool durable,
        string outcome,
        TimeSpan duration)
    {
        var tags = new TagList
        {
            { "job.kind", kind.ToString() },
            { "messaging.backend", durable ? "rabbitmq" : "memory" },
            { "job.outcome", outcome }
        };
        _published.Add(1, tags);
        _publishDuration.Record(duration.TotalMilliseconds, tags);
    }

    public void RecordProcessing(
        BackgroundJobKind kind,
        string outcome,
        TimeSpan duration)
    {
        var tags = new TagList
        {
            { "job.kind", kind.ToString() },
            { "job.outcome", outcome }
        };
        _processed.Add(1, tags);
        _processingDuration.Record(duration.TotalMilliseconds, tags);
    }

    public void RecordCleaned(int count)
    {
        if (count > 0)
            _cleaned.Add(count);
    }

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