using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace StellaOps.Feedser.Source.Vndr.Apple.Internal;

internal sealed record AppleIndexEntry(
    string UpdateId,
    string ArticleId,
    string Title,
    DateTimeOffset PostingDate,
    Uri DetailUri,
    IReadOnlyList<AppleIndexProduct> Products,
    bool IsRapidSecurityResponse);

internal sealed record AppleIndexProduct(
    string Platform,
    string Name,
    string Version,
    string Build);

internal static class AppleIndexParser
{
    private sealed record AppleIndexDocument(
        [property: JsonPropertyName("updates")] IReadOnlyList<AppleIndexEntryDto>? Updates);

    private sealed record AppleIndexEntryDto(
        [property: JsonPropertyName("id")] string? Id,
        [property: JsonPropertyName("articleId")] string? ArticleId,
        [property: JsonPropertyName("title")] string? Title,
        [property: JsonPropertyName("postingDate")] string? PostingDate,
        [property: JsonPropertyName("detailUrl")] string? DetailUrl,
        [property: JsonPropertyName("rapidSecurityResponse")] bool? RapidSecurityResponse,
        [property: JsonPropertyName("products")] IReadOnlyList<AppleIndexProductDto>? Products);

    private sealed record AppleIndexProductDto(
        [property: JsonPropertyName("platform")] string? Platform,
        [property: JsonPropertyName("name")] string? Name,
        [property: JsonPropertyName("version")] string? Version,
        [property: JsonPropertyName("build")] string? Build);

    public static IReadOnlyList<AppleIndexEntry> Parse(ReadOnlySpan<byte> payload, Uri baseUri)
    {
        if (payload.IsEmpty)
        {
            return Array.Empty<AppleIndexEntry>();
        }

        var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
        {
            PropertyNameCaseInsensitive = true,
        };

        AppleIndexDocument? document;
        try
        {
            document = JsonSerializer.Deserialize<AppleIndexDocument>(payload, options);
        }
        catch (JsonException)
        {
            return Array.Empty<AppleIndexEntry>();
        }

        if (document?.Updates is null || document.Updates.Count == 0)
        {
            return Array.Empty<AppleIndexEntry>();
        }

        var entries = new List<AppleIndexEntry>(document.Updates.Count);
        foreach (var dto in document.Updates)
        {
            if (dto is null)
            {
                continue;
            }

            var id = string.IsNullOrWhiteSpace(dto.Id) ? dto.ArticleId : dto.Id;
            if (string.IsNullOrWhiteSpace(id) || string.IsNullOrWhiteSpace(dto.ArticleId))
            {
                continue;
            }

            if (string.IsNullOrWhiteSpace(dto.Title) || string.IsNullOrWhiteSpace(dto.PostingDate))
            {
                continue;
            }

            if (!DateTimeOffset.TryParse(dto.PostingDate, out var postingDate))
            {
                continue;
            }

            if (!TryResolveDetailUri(dto, baseUri, out var detailUri))
            {
                continue;
            }

            var products = dto.Products?.Select(static product => new AppleIndexProduct(
                    product.Platform ?? string.Empty,
                    product.Name ?? product.Platform ?? string.Empty,
                    product.Version ?? string.Empty,
                    product.Build ?? string.Empty))
                .ToArray() ?? Array.Empty<AppleIndexProduct>();

            entries.Add(new AppleIndexEntry(
                id.Trim(),
                dto.ArticleId!.Trim(),
                dto.Title!.Trim(),
                postingDate.ToUniversalTime(),
                detailUri,
                products,
                dto.RapidSecurityResponse ?? false));
        }

        return entries.Count == 0 ? Array.Empty<AppleIndexEntry>() : entries;
    }

    private static bool TryResolveDetailUri(AppleIndexEntryDto dto, Uri baseUri, out Uri uri)
    {
        if (!string.IsNullOrWhiteSpace(dto.DetailUrl) && Uri.TryCreate(dto.DetailUrl, UriKind.Absolute, out uri))
        {
            return true;
        }

        if (string.IsNullOrWhiteSpace(dto.ArticleId))
        {
            uri = default!;
            return false;
        }

        var article = dto.ArticleId.Trim();
        if (article.Length == 0)
        {
            uri = default!;
            return false;
        }

        var combined = new Uri(baseUri, article);
        uri = combined;
        return true;
    }
}
