using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Net;

namespace StellaOps.Feedser.Source.Common.Telemetry;

/// <summary>
/// Central telemetry instrumentation for connector HTTP operations.
/// </summary>
public static class SourceDiagnostics
{
    public const string ActivitySourceName = "StellaOps.Feedser.Source";
    public const string MeterName = "StellaOps.Feedser.Source";

    private static readonly ActivitySource ActivitySource = new(ActivitySourceName);
    private static readonly Meter Meter = new(MeterName);

    private static readonly Counter<long> HttpRequestCounter = Meter.CreateCounter<long>("feedser.source.http.requests");
    private static readonly Counter<long> HttpRetryCounter = Meter.CreateCounter<long>("feedser.source.http.retries");
    private static readonly Counter<long> HttpFailureCounter = Meter.CreateCounter<long>("feedser.source.http.failures");
    private static readonly Counter<long> HttpNotModifiedCounter = Meter.CreateCounter<long>("feedser.source.http.not_modified");
    private static readonly Histogram<double> HttpDuration = Meter.CreateHistogram<double>("feedser.source.http.duration", unit: "ms");
    private static readonly Histogram<long> HttpPayloadBytes = Meter.CreateHistogram<long>("feedser.source.http.payload_bytes", unit: "byte");

    public static Activity? StartFetch(string sourceName, Uri requestUri, string httpMethod, string? clientName)
    {
        var tags = new ActivityTagsCollection
        {
            { "feedser.source", sourceName },
            { "http.method", httpMethod },
            { "http.url", requestUri.ToString() },
        };

        if (!string.IsNullOrWhiteSpace(clientName))
        {
            tags.Add("http.client_name", clientName!);
        }

        return ActivitySource.StartActivity("SourceFetch", ActivityKind.Client, parentContext: default, tags: tags);
    }

    public static void RecordHttpRequest(string sourceName, string? clientName, HttpStatusCode statusCode, int attemptCount, TimeSpan duration, long? contentLength, string? rateLimitRemaining)
    {
        var tags = BuildDefaultTags(sourceName, clientName, statusCode, attemptCount);
        HttpRequestCounter.Add(1, tags);
        HttpDuration.Record(duration.TotalMilliseconds, tags);

        if (contentLength.HasValue && contentLength.Value >= 0)
        {
            HttpPayloadBytes.Record(contentLength.Value, tags);
        }

        if (statusCode == HttpStatusCode.NotModified)
        {
            HttpNotModifiedCounter.Add(1, tags);
        }

        if ((int)statusCode >= 500 || statusCode == HttpStatusCode.TooManyRequests)
        {
            HttpFailureCounter.Add(1, tags);
        }

        if (!string.IsNullOrWhiteSpace(rateLimitRemaining) && long.TryParse(rateLimitRemaining, out var remaining))
        {
            tags.Add("http.rate_limit.remaining", remaining);
        }
    }

    public static void RecordRetry(string sourceName, string? clientName, HttpStatusCode? statusCode, int attempt, TimeSpan delay)
    {
        var tags = new TagList
        {
            { "feedser.source", sourceName },
            { "http.retry_attempt", attempt },
            { "http.retry_delay_ms", delay.TotalMilliseconds },
        };

        if (clientName is not null)
        {
            tags.Add("http.client_name", clientName);
        }

        if (statusCode.HasValue)
        {
            tags.Add("http.status_code", (int)statusCode.Value);
        }

        HttpRetryCounter.Add(1, tags);
    }

    private static TagList BuildDefaultTags(string sourceName, string? clientName, HttpStatusCode statusCode, int attemptCount)
    {
        var tags = new TagList
        {
            { "feedser.source", sourceName },
            { "http.status_code", (int)statusCode },
            { "http.attempts", attemptCount },
        };

        if (clientName is not null)
        {
            tags.Add("http.client_name", clientName);
        }

        return tags;
    }
}
