using System.Text.RegularExpressions;
using Backend.Data;
using Backend.Domain;
using Microsoft.EntityFrameworkCore;

namespace Backend.Services;

public sealed partial class NominationEnrichmentService(
    AwardsDbContext db,
    IViewerStatsProvider viewerStatsProvider,
    NominationTrackingReviewService trackingReviewService)
{
    public async Task EnrichAsync(Nomination nomination, IReadOnlyCollection<Category> groupCategories, CancellationToken cancellationToken)
    {
        if (!TryResolveStreamIdentity(nomination.StreamUrl, out var identity))
        {
            nomination.TrackerStatus = "unresolved";
            nomination.TrackerCheckedAt = DateTimeOffset.UtcNow;
            await trackingReviewService.ReevaluateAsync(nomination, true, cancellationToken);
            return;
        }

        var streamerIdentity = await db.StreamerIdentities
            .FirstOrDefaultAsync(item => item.NormalizedKey == identity.NormalizedKey, cancellationToken);

        if (streamerIdentity is null)
        {
            streamerIdentity = new StreamerIdentity
            {
                Platform = identity.Platform,
                Login = identity.Login,
                NormalizedKey = identity.NormalizedKey,
                DisplayName = identity.DisplayName,
                ProfileUrl = identity.ProfileUrl,
            };
            db.StreamerIdentities.Add(streamerIdentity);
        }
        else
        {
            streamerIdentity.Platform = identity.Platform;
            streamerIdentity.Login = identity.Login;
            streamerIdentity.DisplayName = string.IsNullOrWhiteSpace(streamerIdentity.DisplayName)
                ? identity.DisplayName
                : streamerIdentity.DisplayName;
            streamerIdentity.ProfileUrl ??= identity.ProfileUrl;
        }

        streamerIdentity.LastResolvedAt = DateTimeOffset.UtcNow;
        nomination.StreamerIdentity = streamerIdentity;
        nomination.ResolvedChannel = identity.Login;
        nomination.ResolvedPlatform = identity.Platform;
        nomination.TrackerCheckedAt = DateTimeOffset.UtcNow;

        if (!identity.SupportsViewerStats)
        {
            nomination.TrackerStatus = "unsupported_platform";
            await trackingReviewService.ReevaluateAsync(nomination, true, cancellationToken);
            return;
        }

        var summary = await viewerStatsProvider.GetChannelSummaryAsync(identity.Login, cancellationToken);
        nomination.AvgViewers = summary?.AverageViewers;
        nomination.HoursStreamed = summary?.HoursStreamed;
        nomination.HoursWatched = summary?.HoursWatched;
        nomination.PeakViewers = summary?.PeakViewers;
        nomination.FollowersGained = summary?.FollowersGained;
        nomination.SuggestedCategoryId = nomination.AvgViewers.HasValue
            ? ResolveSuggestedCategoryId(groupCategories, nomination.AvgViewers.Value)
            : null;
        nomination.TrackerStatus = summary is not null ? "resolved" : "no_data";
        await trackingReviewService.ReevaluateAsync(nomination, true, cancellationToken);
    }

    public static bool TryResolveStreamIdentity(string? streamUrl, out ResolvedStreamerIdentity identity)
    {
        identity = default;
        if (string.IsNullOrWhiteSpace(streamUrl) || !Uri.TryCreate(streamUrl.Trim(), UriKind.Absolute, out var uri))
        {
            return false;
        }

        var host = uri.Host.Replace("www.", string.Empty, StringComparison.OrdinalIgnoreCase).ToLowerInvariant();
        var pathParts = uri.AbsolutePath
            .Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
            .Select(part => part.TrimStart('@'))
            .Where(part => !IgnoredPathParts.Contains(part, StringComparer.OrdinalIgnoreCase))
            .ToArray();
        var login = pathParts.FirstOrDefault() ?? string.Empty;

        if (host.Contains("youtu.be", StringComparison.OrdinalIgnoreCase))
        {
            login = pathParts.FirstOrDefault() ?? uri.Host;
        }

        var platform = ResolvePlatform(host);
        login = SanitizeLogin(login);
        if (string.IsNullOrWhiteSpace(login))
        {
            return false;
        }

        var profileUrl = BuildProfileUrl(platform, login, uri);
        identity = new ResolvedStreamerIdentity(
            platform,
            login,
            $"{platform.ToLowerInvariant()}:{login.ToLowerInvariant()}",
            login,
            profileUrl,
            string.Equals(platform, "Twitch", StringComparison.OrdinalIgnoreCase));
        return true;
    }

    private static int? ResolveSuggestedCategoryId(IEnumerable<Category> groupCategories, int averageViewers)
    {
        var orderedCategories = groupCategories
            .OrderBy(item => item.SortOrder)
            .ToArray();

        var rangedCategories = orderedCategories
            .Where(category => category.ViewerRangeMin.HasValue || category.ViewerRangeMax.HasValue)
            .ToArray();

        if (rangedCategories.Length > 0)
        {
            return rangedCategories
                .FirstOrDefault(category =>
                    (!category.ViewerRangeMin.HasValue || averageViewers >= category.ViewerRangeMin.Value)
                    && (!category.ViewerRangeMax.HasValue || averageViewers <= category.ViewerRangeMax.Value))
                ?.Id;
        }

        return orderedCategories
            .FirstOrDefault()
            ?.Id;
    }

    private static string ResolvePlatform(string host)
    {
        if (host.Contains("twitch.tv", StringComparison.OrdinalIgnoreCase)) return "Twitch";
        if (host.Contains("youtube.com", StringComparison.OrdinalIgnoreCase) || host.Contains("youtu.be", StringComparison.OrdinalIgnoreCase)) return "YouTube";
        if (host.Contains("kick.com", StringComparison.OrdinalIgnoreCase)) return "Kick";

        var firstPart = host.Split('.', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
        return string.IsNullOrWhiteSpace(firstPart)
            ? "Website"
            : $"{char.ToUpperInvariant(firstPart[0])}{firstPart[1..]}";
    }

    private static string BuildProfileUrl(string platform, string login, Uri originalUrl) =>
        platform.ToLowerInvariant() switch
        {
            "twitch" => $"https://twitch.tv/{login}",
            "youtube" => $"https://youtube.com/{login}",
            "kick" => $"https://kick.com/{login}",
            _ => originalUrl.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped),
        };

    private static string SanitizeLogin(string value) =>
        LoginRegex().Replace(value.Trim().TrimStart('@'), string.Empty);

    private static readonly string[] IgnoredPathParts = ["c", "channel", "user", "live", "videos", "video", "clip", "clips", "directory"];

    [GeneratedRegex("[^a-zA-Z0-9._-]", RegexOptions.Compiled)]
    private static partial Regex LoginRegex();
}

public readonly record struct ResolvedStreamerIdentity(
    string Platform,
    string Login,
    string NormalizedKey,
    string DisplayName,
    string ProfileUrl,
    bool SupportsViewerStats);
