using Backend.Common;
using Backend.Contracts;
using Backend.Data;
using Backend.Services;
using Microsoft.EntityFrameworkCore;

namespace Backend.Endpoints;

public static partial class AdminModerationEndpoints
{
    private static async Task<IResult> GetNominationLinkBlacklist(AwardsDbContext db)
    {
        var settings = await db.SiteSettings.AsNoTracking().FirstOrDefaultAsync(item => item.Id == 1);
        if (settings is null)
        {
            return Results.NotFound();
        }

        return Results.Ok(ToNominationLinkBlacklistResponse(settings));
    }

    private static async Task<IResult> UpdateNominationLinkBlacklist(
        HttpContext context,
        UpdateNominationLinkBlacklistRequest request,
        AwardsDbContext db,
        IAdminAuditService adminAuditService)
    {
        var session = AdminEndpointConventions.CurrentSession(context);
        var settings = await db.SiteSettings.FirstOrDefaultAsync(item => item.Id == 1);
        if (settings is null)
        {
            return Results.NotFound();
        }

        var normalizedEntries = NormalizeBlacklistEntries(request.Urls, out var invalidUrl);
        if (invalidUrl is not null)
        {
            return Results.BadRequest(new { message = $"Blacklist-Link ist keine gueltige http(s)-URL: {invalidUrl}" });
        }

        settings.NominationLinkBlacklistJson = NominationLinkBlacklistSettings.Serialize(normalizedEntries);
        adminAuditService.AddEntry(
            session.TwitchUserId,
            "nomination-link-blacklist.update",
            "site-settings",
            settings.Id.ToString(),
            "Nominierungs-Link-Blacklist wurde aktualisiert.",
            new { count = normalizedEntries.Length },
            RequestMetadataReader.Read(context));

        await db.SaveChangesAsync(context.RequestAborted);
        return Results.Ok(ToNominationLinkBlacklistResponse(settings));
    }

    private static async Task<IResult> AddNominationLinkBlacklistEntry(
        HttpContext context,
        AddNominationLinkBlacklistEntryRequest request,
        AwardsDbContext db,
        IAdminAuditService adminAuditService)
    {
        var session = AdminEndpointConventions.CurrentSession(context);
        var settings = await db.SiteSettings.FirstOrDefaultAsync(item => item.Id == 1);
        if (settings is null)
        {
            return Results.NotFound();
        }

        if (!NominationLinkBlacklistSettings.TryNormalizeUrl(request.Url, out var normalizedUrl))
        {
            return Results.BadRequest(new { message = "Blacklist-Link ist keine gueltige http(s)-URL." });
        }

        var entries = NominationLinkBlacklistSettings.Read(settings).ToList();
        if (!NominationLinkBlacklistSettings.IsBlocked(normalizedUrl, entries))
        {
            entries.Add(new NominationLinkBlacklistEntry(normalizedUrl));
            settings.NominationLinkBlacklistJson = NominationLinkBlacklistSettings.Serialize(entries);

            adminAuditService.AddEntry(
                session.TwitchUserId,
                "nomination-link-blacklist.add",
                "site-settings",
                settings.Id.ToString(),
                "Link wurde zur Nominierungs-Blacklist hinzugefuegt.",
                new { url = normalizedUrl },
                RequestMetadataReader.Read(context));

            await db.SaveChangesAsync(context.RequestAborted);
        }

        return Results.Ok(ToNominationLinkBlacklistResponse(settings));
    }

    private static AdminNominationLinkBlacklistResponse ToNominationLinkBlacklistResponse(Backend.Domain.SiteSettings settings) =>
        new(NominationLinkBlacklistSettings.Read(settings)
            .Select(entry => new AdminNominationLinkBlacklistEntryDto(entry.Url))
            .ToArray());

    private static NominationLinkBlacklistEntry[] NormalizeBlacklistEntries(string[]? urls, out string? invalidUrl)
    {
        invalidUrl = null;
        var entries = new List<NominationLinkBlacklistEntry>();
        foreach (var rawUrl in urls ?? [])
        {
            if (string.IsNullOrWhiteSpace(rawUrl))
            {
                continue;
            }

            if (!NominationLinkBlacklistSettings.TryNormalizeUrl(rawUrl, out var normalizedUrl))
            {
                invalidUrl = rawUrl;
                return [];
            }

            entries.Add(new NominationLinkBlacklistEntry(normalizedUrl));
        }

        return entries
            .DistinctBy(entry => entry.Url, StringComparer.OrdinalIgnoreCase)
            .ToArray();
    }
}
