using System.Globalization;

namespace Myriad.Rest.Ratelimit;

public record RatelimitHeaders
{
    private const string LimitHeader = "X-RateLimit-Limit";
    private const string RemainingHeader = "X-RateLimit-Remaining";
    private const string ResetHeader = "X-RateLimit-Reset";
    private const string ResetAfterHeader = "X-RateLimit-Reset-After";
    private const string BucketHeader = "X-RateLimit-Bucket";
    private const string GlobalHeader = "X-RateLimit-Global";

    public bool Global { get; private set; }
    public int? Limit { get; private set; }
    public int? Remaining { get; private set; }
    public DateTimeOffset? Reset { get; private set; }
    public TimeSpan? ResetAfter { get; private set; }
    public string? Bucket { get; private set; }

    public DateTimeOffset? ServerDate { get; private set; }

    public bool HasRatelimitInfo =>
        Limit != null && Remaining != null && Reset != null && ResetAfter != null && Bucket != null;

    public static RatelimitHeaders Parse(HttpResponseMessage response)
    {
        var headers = new RatelimitHeaders
        {
            ServerDate = response.Headers.Date,
            Limit = TryGetInt(response, LimitHeader),
            Remaining = TryGetInt(response, RemainingHeader),
            Bucket = TryGetHeader(response, BucketHeader)
        };


        var resetTimestamp = TryGetDouble(response, ResetHeader);
        if (resetTimestamp != null)
            headers.Reset = DateTimeOffset.FromUnixTimeMilliseconds((long)(resetTimestamp.Value * 1000));

        var resetAfterSeconds = TryGetDouble(response, ResetAfterHeader);
        if (resetAfterSeconds != null)
            headers.ResetAfter = TimeSpan.FromSeconds(resetAfterSeconds.Value);

        var global = TryGetHeader(response, GlobalHeader);
        if (global != null && bool.TryParse(global, out var globalBool))
            headers.Global = globalBool;

        return headers;
    }

    private static string? TryGetHeader(HttpResponseMessage response, string headerName)
    {
        if (!response.Headers.TryGetValues(headerName, out var values))
            return null;

        return values.FirstOrDefault();
    }

    private static int? TryGetInt(HttpResponseMessage response, string headerName)
    {
        var valueString = TryGetHeader(response, headerName);

        if (!int.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
            return null;

        return value;
    }

    private static double? TryGetDouble(HttpResponseMessage response, string headerName)
    {
        var valueString = TryGetHeader(response, headerName);

        if (!double.TryParse(valueString, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
            return null;

        return value;
    }
}