using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using KArtSell.BuildingBlocks.Time;

namespace KArtSell.Host.Observability;

/// <summary>
/// In-memory API call metrics tracking (24h retention).
/// Records: success/failure, latency, retry count, rate limiting, quota remaining.
/// </summary>
public sealed class ApiCallMetricsService : IDisposable
{
    private readonly ConcurrentDictionary<string, ApiMetric> _metrics = new();
    private readonly ILogger<ApiCallMetricsService> _logger;
    private readonly IClock _clock;
    private readonly Timer _cleanupTimer;

    public ApiCallMetricsService(ILogger<ApiCallMetricsService> logger, IClock clock)
    {
        _logger = logger;
        _clock = clock;
        // Cleanup old entries every hour
        _cleanupTimer = new Timer(CleanupOldEntries, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
    }

    public void Dispose()
    {
        _cleanupTimer?.Dispose();
    }

    public void RecordApiCall(
        string apiName,
        bool success,
        int latencyMs = 0,
        int retryCount = 0,
        bool rateLimited = false,
        int? remainingQuota = null)
    {
        var key = $"{apiName}:{_clock.UtcNow:yyyy-MM-dd HH:mm}";

        _metrics.AddOrUpdate(key, _ =>
            new ApiMetric
            {
                ApiName = apiName,
                Timestamp = _clock.UtcNow,
                Success = success,
                LatencyMs = latencyMs,
                RetryCount = retryCount,
                RateLimited = rateLimited,
                RemainingQuota = remainingQuota
            },
            (_, existing) => existing);  // Keep first entry per minute

        if (rateLimited)
        {
            _logger.LogWarning(
                "API rate limited: {ApiName}, remaining: {Quota}, retry: {RetryCount}",
                apiName, remainingQuota, retryCount);
        }
    }

    public IReadOnlyList<ApiMetric> GetMetrics(string? apiNameFilter = null)
    {
        var results = _metrics.Values.AsEnumerable();

        if (!string.IsNullOrEmpty(apiNameFilter))
        {
            results = results.Where(m => m.ApiName.Contains(apiNameFilter, StringComparison.OrdinalIgnoreCase));
        }

        return results.OrderByDescending(m => m.Timestamp).ToList().AsReadOnly();
    }

    public Dictionary<string, ApiSummary> GetSummary()
    {
        var summary = new Dictionary<string, ApiSummary>();

        foreach (var group in _metrics.Values.GroupBy(m => m.ApiName))
        {
            var metrics = group.ToList();
            summary[group.Key] = new ApiSummary
            {
                TotalCalls = metrics.Count,
                SuccessCount = metrics.Count(m => m.Success),
                FailureCount = metrics.Count(m => !m.Success),
                RateLimitCount = metrics.Count(m => m.RateLimited),
                AverageLatencyMs = metrics.Average(m => m.LatencyMs),
                MinRemainingQuota = metrics.Where(m => m.RemainingQuota.HasValue).Min(m => m.RemainingQuota),
                LastUpdated = metrics.Max(m => m.Timestamp)
            };
        }

        return summary;
    }

    private void CleanupOldEntries(object? state)
    {
        var cutoff = _clock.UtcNow.AddHours(-24);
        var oldKeys = _metrics.Where(kvp => kvp.Value.Timestamp < cutoff).Select(kvp => kvp.Key).ToList();

        foreach (var key in oldKeys)
        {
            _metrics.TryRemove(key, out _);
        }

        if (oldKeys.Count > 0)
        {
            _logger.LogDebug("Cleaned up {Count} old API metrics", oldKeys.Count);
        }
    }
}

public sealed record ApiMetric
{
    public required string ApiName { get; init; }
    public required DateTimeOffset Timestamp { get; init; }
    public required bool Success { get; init; }
    public required int LatencyMs { get; init; }
    public required int RetryCount { get; init; }
    public required bool RateLimited { get; init; }
    public required int? RemainingQuota { get; init; }
}

public sealed record ApiSummary
{
    public required int TotalCalls { get; init; }
    public required int SuccessCount { get; init; }
    public required int FailureCount { get; init; }
    public required int RateLimitCount { get; init; }
    public required double AverageLatencyMs { get; init; }
    public required int? MinRemainingQuota { get; init; }
    public required DateTimeOffset LastUpdated { get; init; }
}
