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

namespace Backend.Endpoints;

public static partial class AdminSeasonManagementEndpoints
{
    private const int MaxCategoryGroupNameLength = 80;
    private const int MaxCategoryNameLength = 120;
    private const int MaxCategorySlugLength = 120;
    private const int MaxCategoryDescriptionLength = 600;
    private const int MaxCandidateDisplayNameLength = 120;
    private const int MaxCandidateChannelSlugLength = 120;
    private const int MaxCandidatePlatformLength = 60;

    private static IResult? ValidateSeasonRequest(CreateSeasonRequest request)
    {
        if (request.Year < 2020 || request.Year > 2100)
        {
            return Results.BadRequest(new { message = "Please provide a valid award year." });
        }

        if (string.IsNullOrWhiteSpace(request.Name) || string.IsNullOrWhiteSpace(request.CurrentPhase))
        {
            return Results.BadRequest(new { message = "Season name and current phase are required." });
        }

        if (!IsKnownSeasonPhase(request.CurrentPhase))
        {
            return Results.BadRequest(new { message = "Current phase must be nomination, voting, preparation, show, or completed." });
        }

        if (!SeasonMappings.IsSeasonScheduleValid(
                request.NominationStartsAt,
                request.NominationEndsAt,
                request.VotingStartsAt,
                request.VotingEndsAt,
                request.ReviewStartsAt,
                request.ReviewEndsAt,
                request.ShowDate))
        {
            return Results.BadRequest(new { message = "The season schedule is not in chronological order." });
        }

        return null;
    }

    private static IResult? ValidateSeasonRequest(UpdateSeasonRequest request)
    {
        if (request.Year < 2020 || request.Year > 2100)
        {
            return Results.BadRequest(new { message = "Please provide a valid award year." });
        }

        if (string.IsNullOrWhiteSpace(request.Name) || string.IsNullOrWhiteSpace(request.CurrentPhase))
        {
            return Results.BadRequest(new { message = "Season name and current phase are required." });
        }

        if (!IsKnownSeasonPhase(request.CurrentPhase))
        {
            return Results.BadRequest(new { message = "Current phase must be nomination, voting, preparation, show, or completed." });
        }

        if (!SeasonMappings.IsSeasonScheduleValid(
                request.NominationStartsAt,
                request.NominationEndsAt,
                request.VotingStartsAt,
                request.VotingEndsAt,
                request.ReviewStartsAt,
                request.ReviewEndsAt,
                request.ShowDate))
        {
            return Results.BadRequest(new { message = "The season schedule is not in chronological order." });
        }

        return null;
    }

    private static string NormalizeSeasonStreamUrl(string? showStreamUrl)
    {
        return SeasonMappings.NormalizeSeasonStreamUrl(showStreamUrl);
    }

    private static bool IsKnownSeasonPhase(string? currentPhase)
    {
        var value = currentPhase?.Trim().ToLowerInvariant() ?? string.Empty;
        return value.Contains("show")
            || value.Contains("abgeschlossen")
            || value.Contains("archiv")
            || value.Contains("complete")
            || value.Contains("ended")
            || value.Contains("aufbereit")
            || value.Contains("vorbereit")
            || value.Contains("pause")
            || value.Contains("review")
            || value.Contains("auswert")
            || value.Contains("vot")
            || value.Contains("nomin");
    }

    private static async Task UnsetOtherCurrentSeasonsAsync(
        AwardsDbContext db,
        bool shouldUnsetOthers,
        int? seasonIdToKeep,
        CancellationToken cancellationToken)
    {
        if (!shouldUnsetOthers)
        {
            return;
        }

        var activeSeasons = await db.Seasons
            .Where(item => item.IsCurrent && (!seasonIdToKeep.HasValue || item.Id != seasonIdToKeep.Value))
            .ToListAsync(cancellationToken);

        foreach (var activeSeason in activeSeasons)
        {
            activeSeason.IsCurrent = false;
        }
    }

    private static IResult CreateReadinessError(IEnumerable<string> issues)
    {
        var issueList = issues.ToArray();
        return Results.BadRequest(new
        {
            message = $"Public-/Archiv-Readiness blockiert: {string.Join(" ", issueList)}",
            issues = issueList,
        });
    }

    private static string[] BuildNewSeasonReadinessIssues(
        string currentPhase,
        bool isCurrent,
        int copiedCategoryCount)
    {
        var phaseKey = SeasonMappings.NormalizePhaseKey(currentPhase);
        var needsCandidateReadiness = RequiresCandidateReadiness(phaseKey, isCurrent);
        var needsWinnerReadiness = RequiresWinnerReadiness(phaseKey);
        if (!isCurrent && !needsWinnerReadiness)
        {
            return [];
        }

        var issues = new List<string>();
        if (copiedCategoryCount <= 0)
        {
            issues.Add("Mindestens eine Kategorie ist erforderlich.");
        }

        if (needsCandidateReadiness)
        {
            issues.Add("Kandidaten muessen vor dieser Phase fuer alle Kategorien gepflegt sein.");
        }

        if (needsWinnerReadiness)
        {
            issues.Add("Abgeschlossen ist erst moeglich, wenn jede Kategorie einen Gewinner hat.");
        }

        return issues.ToArray();
    }

    private static async Task<string[]> BuildSeasonReadinessIssuesAsync(
        AwardsDbContext db,
        int seasonId,
        string currentPhase,
        bool isCurrent,
        CancellationToken cancellationToken)
    {
        var phaseKey = SeasonMappings.NormalizePhaseKey(currentPhase);
        var needsCandidateReadiness = RequiresCandidateReadiness(phaseKey, isCurrent);
        var needsWinnerReadiness = RequiresWinnerReadiness(phaseKey);
        if (!isCurrent && !needsWinnerReadiness)
        {
            return [];
        }

        var categoryIds = await db.Categories
            .AsNoTracking()
            .Where(item => item.SeasonId == seasonId)
            .Select(item => item.Id)
            .ToArrayAsync(cancellationToken);

        var issues = new List<string>();
        if (categoryIds.Length == 0)
        {
            issues.Add("Mindestens eine Kategorie ist erforderlich.");
        }

        if (needsCandidateReadiness && categoryIds.Length > 0)
        {
            var categoriesWithCandidates = await db.Candidates
                .AsNoTracking()
                .Where(item => item.SeasonId == seasonId)
                .Select(item => item.CategoryId)
                .Distinct()
                .CountAsync(cancellationToken);
            var emptyCategories = Math.Max(0, categoryIds.Length - categoriesWithCandidates);
            if (emptyCategories > 0)
            {
                issues.Add($"{emptyCategories} Kategorien haben noch keine Kandidaten.");
            }
        }

        if (needsWinnerReadiness && categoryIds.Length > 0)
        {
            var categoriesWithResults = await db.Results
                .AsNoTracking()
                .Where(item => item.SeasonId == seasonId)
                .Select(item => item.CategoryId)
                .Distinct()
                .CountAsync(cancellationToken);
            var missingResults = Math.Max(0, categoryIds.Length - categoriesWithResults);
            if (missingResults > 0)
            {
                issues.Add($"{missingResults} Kategorien haben noch keinen Gewinner.");
            }
        }

        return issues.ToArray();
    }

    private static bool RequiresCandidateReadiness(string phaseKey, bool isCurrent)
    {
        return (isCurrent && !string.Equals(phaseKey, "nomination", StringComparison.Ordinal))
            || string.Equals(phaseKey, "completed", StringComparison.Ordinal);
    }

    private static bool RequiresWinnerReadiness(string phaseKey)
    {
        return string.Equals(phaseKey, "completed", StringComparison.Ordinal);
    }

    private static IResult? ValidateCategoryRequest(UpsertCategoryRequest request)
    {
        var groupName = request.GroupName.Trim();
        var name = request.Name.Trim();
        var slug = request.Slug.Trim();
        var description = request.Description.Trim();

        if (string.IsNullOrWhiteSpace(groupName) || groupName.Length > MaxCategoryGroupNameLength)
        {
            return Results.BadRequest(new { message = $"Category group name is required and must stay below {MaxCategoryGroupNameLength} characters." });
        }

        if (string.IsNullOrWhiteSpace(name) || name.Length > MaxCategoryNameLength)
        {
            return Results.BadRequest(new { message = $"Category name is required and must stay below {MaxCategoryNameLength} characters." });
        }

        if (string.IsNullOrWhiteSpace(slug) || slug.Length > MaxCategorySlugLength)
        {
            return Results.BadRequest(new { message = $"Category slug is required and must stay below {MaxCategorySlugLength} characters." });
        }

        if (!slug.All(value => char.IsLetterOrDigit(value) || value is '-' or '_'))
        {
            return Results.BadRequest(new { message = "Category slug contains unsupported characters." });
        }

        if (description.Length > MaxCategoryDescriptionLength)
        {
            return Results.BadRequest(new { message = $"Category description must stay below {MaxCategoryDescriptionLength} characters." });
        }

        if (request.SortOrder is < 0 or > 500)
        {
            return Results.BadRequest(new { message = "Category sort order must be between 0 and 500." });
        }

        if (request.MaxNomineesPerUser is < 1 or > 10)
        {
            return Results.BadRequest(new { message = "Max nominees per user must be between 1 and 10." });
        }

        return null;
    }

    private static IResult? ValidateCandidateRequest(UpsertCandidateRequest request)
    {
        var displayName = request.DisplayName.Trim();
        var channelSlug = request.ChannelSlug.Trim();
        var platform = request.Platform.Trim();

        if (request.CategoryId <= 0)
        {
            return Results.BadRequest(new { message = "A valid category is required." });
        }

        if (string.IsNullOrWhiteSpace(displayName) || displayName.Length > MaxCandidateDisplayNameLength)
        {
            return Results.BadRequest(new { message = $"Display name is required and must stay below {MaxCandidateDisplayNameLength} characters." });
        }

        if (string.IsNullOrWhiteSpace(channelSlug) || channelSlug.Length > MaxCandidateChannelSlugLength)
        {
            return Results.BadRequest(new { message = $"Channel slug is required and must stay below {MaxCandidateChannelSlugLength} characters." });
        }

        if (!channelSlug.All(value => char.IsLetterOrDigit(value) || value is '-' or '_' or '.'))
        {
            return Results.BadRequest(new { message = "Channel slug contains unsupported characters." });
        }

        if (string.IsNullOrWhiteSpace(platform) || platform.Length > MaxCandidatePlatformLength)
        {
            return Results.BadRequest(new { message = $"Platform is required and must stay below {MaxCandidatePlatformLength} characters." });
        }

        return null;
    }
}
