using System.Text.Json;
using Backend.Domain;

namespace Backend.Services;

public sealed record NominationLinkBlacklistEntry(string Url);

public static class NominationLinkBlacklistSettings
{
    public static readonly NominationLinkBlacklistEntry[] Defaults =
    [
        new("https://www.twitch.tv/"),
        new("https://kick.com/"),
        new("https://www.youtube.com/"),
    ];

    public static NominationLinkBlacklistEntry[] Read(SiteSettings? settings)
    {
        var entries = Parse(settings?.NominationLinkBlacklistJson);
        return entries.Length > 0 ? entries : Defaults;
    }

    public static string Serialize(IEnumerable<NominationLinkBlacklistEntry> entries)
    {
        var normalizedEntries = entries
            .Select(entry => NormalizeEntry(entry.Url))
            .Where(entry => entry is not null)
            .Select(entry => new NominationLinkBlacklistEntry(entry!))
            .DistinctBy(entry => BuildComparisonKey(entry.Url), StringComparer.OrdinalIgnoreCase)
            .OrderBy(entry => entry.Url, StringComparer.OrdinalIgnoreCase)
            .ToArray();

        return JsonSerializer.Serialize(normalizedEntries);
    }

    public static bool TryNormalizeUrl(string? rawUrl, out string normalizedUrl)
    {
        normalizedUrl = NormalizeEntry(rawUrl) ?? string.Empty;
        return !string.IsNullOrWhiteSpace(normalizedUrl);
    }

    public static bool IsBlocked(string rawUrl, IEnumerable<NominationLinkBlacklistEntry> entries)
    {
        var submittedKey = BuildComparisonKey(rawUrl);
        return !string.IsNullOrWhiteSpace(submittedKey)
            && entries.Any(entry => string.Equals(BuildComparisonKey(entry.Url), submittedKey, StringComparison.OrdinalIgnoreCase));
    }

    private static NominationLinkBlacklistEntry[] Parse(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
        {
            return [];
        }

        try
        {
            var entries = JsonSerializer.Deserialize<NominationLinkBlacklistEntry[]>(json);
            return entries?.Where(entry => !string.IsNullOrWhiteSpace(entry.Url)).ToArray() ?? [];
        }
        catch (JsonException)
        {
            return [];
        }
    }

    private static string? NormalizeEntry(string? rawUrl)
    {
        if (string.IsNullOrWhiteSpace(rawUrl))
        {
            return null;
        }

        var candidate = rawUrl.Trim();
        if (!candidate.Contains("://", StringComparison.Ordinal))
        {
            candidate = $"https://{candidate}";
        }

        if (!Uri.TryCreate(candidate, UriKind.Absolute, out var uri))
        {
            return null;
        }

        if (uri.Scheme is not ("http" or "https") || string.IsNullOrWhiteSpace(uri.Host))
        {
            return null;
        }

        var builder = new UriBuilder(uri)
        {
            Scheme = Uri.UriSchemeHttps,
            Host = uri.Host.ToLowerInvariant(),
            Port = -1,
            Query = string.Empty,
            Fragment = string.Empty,
        };

        var normalized = builder.Uri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped);
        return normalized.EndsWith('/') ? normalized : $"{normalized}/";
    }

    private static string BuildComparisonKey(string? rawUrl)
    {
        if (NormalizeEntry(rawUrl) is not { } normalized)
        {
            return string.Empty;
        }

        if (!Uri.TryCreate(normalized, UriKind.Absolute, out var uri))
        {
            return string.Empty;
        }

        var host = uri.Host.StartsWith("www.", StringComparison.OrdinalIgnoreCase)
            ? uri.Host[4..]
            : uri.Host;
        var path = uri.AbsolutePath.TrimEnd('/');
        return $"{host.ToLowerInvariant()}{path.ToLowerInvariant()}";
    }
}
