using System;
using System.Collections.Generic;
using System.Globalization;

namespace StellaOps.Feedser.Source.Ghsa.Internal;

internal static class GhsaRateLimitParser
{
    public static GhsaRateLimitSnapshot? TryParse(IReadOnlyDictionary<string, string>? headers, DateTimeOffset now, string phase)
    {
        if (headers is null || headers.Count == 0)
        {
            return null;
        }

        string? resource = null;
        long? limit = null;
        long? remaining = null;
        long? used = null;
        DateTimeOffset? resetAt = null;
        TimeSpan? resetAfter = null;
        TimeSpan? retryAfter = null;
        var hasData = false;

        if (TryGet(headers, "X-RateLimit-Resource", out var resourceValue) && !string.IsNullOrWhiteSpace(resourceValue))
        {
            resource = resourceValue;
            hasData = true;
        }

        if (TryParseLong(headers, "X-RateLimit-Limit", out var limitValue))
        {
            limit = limitValue;
            hasData = true;
        }

        if (TryParseLong(headers, "X-RateLimit-Remaining", out var remainingValue))
        {
            remaining = remainingValue;
            hasData = true;
        }

        if (TryParseLong(headers, "X-RateLimit-Used", out var usedValue))
        {
            used = usedValue;
            hasData = true;
        }

        if (TryParseLong(headers, "X-RateLimit-Reset", out var resetValue))
        {
            resetAt = DateTimeOffset.FromUnixTimeSeconds(resetValue);
            var delta = resetAt.Value - now;
            if (delta > TimeSpan.Zero)
            {
                resetAfter = delta;
            }
            hasData = true;
        }

        if (TryGet(headers, "Retry-After", out var retryAfterValue) && !string.IsNullOrWhiteSpace(retryAfterValue))
        {
            if (double.TryParse(retryAfterValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var seconds) && seconds > 0)
            {
                retryAfter = TimeSpan.FromSeconds(seconds);
            }
            else if (DateTimeOffset.TryParse(retryAfterValue, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var retryAfterDate))
            {
                var delta = retryAfterDate - now;
                if (delta > TimeSpan.Zero)
                {
                    retryAfter = delta;
                }
            }
            hasData = true;
        }

        if (!hasData)
        {
            return null;
        }

        return new GhsaRateLimitSnapshot(phase, resource, limit, remaining, used, resetAt, resetAfter, retryAfter);
    }

    private static bool TryGet(IReadOnlyDictionary<string, string> headers, string key, out string value)
    {
        foreach (var pair in headers)
        {
            if (pair.Key.Equals(key, StringComparison.OrdinalIgnoreCase))
            {
                value = pair.Value;
                return true;
            }
        }

        value = string.Empty;
        return false;
    }

    private static bool TryParseLong(IReadOnlyDictionary<string, string> headers, string key, out long result)
    {
        result = 0;
        if (TryGet(headers, key, out var value) && long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsed))
        {
            result = parsed;
            return true;
        }

        return false;
    }
}
