using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;

namespace StellaOps.Feedser.Source.Ghsa.Internal;

internal static class GhsaListParser
{
    public static GhsaListPage Parse(ReadOnlySpan<byte> content, int currentPage, int pageSize)
    {
        using var document = JsonDocument.Parse(content.ToArray());
        var root = document.RootElement;

        var items = new List<GhsaListItem>();
        DateTimeOffset? maxUpdated = null;

        if (root.TryGetProperty("advisories", out var advisories) && advisories.ValueKind == JsonValueKind.Array)
        {
            foreach (var advisory in advisories.EnumerateArray())
            {
                if (advisory.ValueKind != JsonValueKind.Object)
                {
                    continue;
                }

                var id = GetString(advisory, "ghsa_id");
                if (string.IsNullOrWhiteSpace(id))
                {
                    continue;
                }

                var updated = GetDate(advisory, "updated_at");
                if (updated.HasValue && (!maxUpdated.HasValue || updated > maxUpdated))
                {
                    maxUpdated = updated;
                }

                items.Add(new GhsaListItem(id, updated));
            }
        }

        var hasMorePages = TryDetermineHasMore(root, currentPage, pageSize, items.Count, out var nextPage);

        return new GhsaListPage(items, maxUpdated, hasMorePages, nextPage ?? currentPage + 1);
    }

    private static bool TryDetermineHasMore(JsonElement root, int currentPage, int pageSize, int itemCount, out int? nextPage)
    {
        nextPage = null;

        if (root.TryGetProperty("pagination", out var pagination) && pagination.ValueKind == JsonValueKind.Object)
        {
            var hasNextPage = pagination.TryGetProperty("has_next_page", out var hasNext) && hasNext.ValueKind == JsonValueKind.True;
            if (hasNextPage)
            {
                nextPage = currentPage + 1;
                return true;
            }

            if (pagination.TryGetProperty("total_pages", out var totalPagesElement) && totalPagesElement.ValueKind == JsonValueKind.Number && totalPagesElement.TryGetInt32(out var totalPages))
            {
                if (currentPage < totalPages)
                {
                    nextPage = currentPage + 1;
                    return true;
                }
            }

            return false;
        }

        if (itemCount >= pageSize)
        {
            nextPage = currentPage + 1;
            return true;
        }

        return false;
    }

    private static string? GetString(JsonElement element, string propertyName)
    {
        if (!element.TryGetProperty(propertyName, out var property))
        {
            return null;
        }

        return property.ValueKind switch
        {
            JsonValueKind.String => property.GetString(),
            _ => null,
        };
    }

    private static DateTimeOffset? GetDate(JsonElement element, string propertyName)
    {
        var value = GetString(element, propertyName);
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var parsed)
            ? parsed.ToUniversalTime()
            : null;
    }
}

internal sealed record GhsaListPage(
    IReadOnlyList<GhsaListItem> Items,
    DateTimeOffset? MaxUpdated,
    bool HasMorePages,
    int NextPageCandidate);

internal sealed record GhsaListItem(string GhsaId, DateTimeOffset? UpdatedAt);
