using Backend.Data;
using Backend.Domain;
using Microsoft.EntityFrameworkCore;

namespace Backend.Services;

public sealed class NominationTrackingReviewService(AwardsDbContext db)
{
    public async Task ReevaluateAsync(Nomination nomination, bool resetManualResolution, CancellationToken cancellationToken)
    {
        var settings = await db.SiteSettings
            .AsNoTracking()
            .FirstOrDefaultAsync(item => item.Id == 1, cancellationToken);

        ApplyEvaluation(nomination, TrackingRulesSettings.Read(settings), resetManualResolution);
    }

    public async Task ReevaluateAllAsync(bool resetManualResolution, CancellationToken cancellationToken)
    {
        var settings = await db.SiteSettings
            .AsNoTracking()
            .FirstOrDefaultAsync(item => item.Id == 1, cancellationToken);

        var rules = TrackingRulesSettings.Read(settings);
        await ReevaluateAllAsync(rules, resetManualResolution, cancellationToken);
    }

    public async Task ReevaluateAllAsync(
        TrackingRulesConfiguration rules,
        bool resetManualResolution,
        CancellationToken cancellationToken)
    {
        var nominations = await db.Nominations.ToArrayAsync(cancellationToken);
        foreach (var nomination in nominations)
        {
            ApplyEvaluation(nomination, rules, resetManualResolution);
        }
    }

    public NominationTrackingEvaluation Evaluate(Nomination nomination, TrackingRulesConfiguration rules)
    {
        var requiredMetrics = rules.ImportantMetrics
            .Where(item => item.Enabled && item.RequiredForAutoClassification)
            .ToArray();
        var missingRequiredMetrics = requiredMetrics
            .Where(metric => !IsMetricPresent(metric, nomination))
            .ToArray();
        var unsupportedAutomaticWindows = rules.ImportantMetrics
            .Concat(rules.OptionalMetrics)
            .Where(item => item.Enabled && item.SourceSupport == "auto" && !TrackingRulesSettings.SupportsAutomaticWindow(item))
            .ToArray();

        var triggeredFlags = new List<TrackingFlagHit>();
        AddFlagIf(triggeredFlags, rules.Flags, TrackingRulesSettings.FlagTrackerUnresolved, nomination.TrackerStatus == "unresolved");
        AddFlagIf(triggeredFlags, rules.Flags, TrackingRulesSettings.FlagUnsupportedPlatform, nomination.TrackerStatus == "unsupported_platform");
        AddFlagIf(triggeredFlags, rules.Flags, TrackingRulesSettings.FlagNoTrackerData, nomination.TrackerStatus == "no_data");
        AddFlagIf(triggeredFlags, rules.Flags, TrackingRulesSettings.FlagMissingRequiredMetric, missingRequiredMetrics.Length > 0);
        AddFlagIf(triggeredFlags, rules.Flags, TrackingRulesSettings.FlagUnsupportedMetricWindow, unsupportedAutomaticWindows.Length > 0);

        var needsManualReview = triggeredFlags.Any(flag => flag.RequiresManualReview);
        AddFlagIf(triggeredFlags, rules.Flags, TrackingRulesSettings.FlagManualReviewRequired, needsManualReview);

        return new NominationTrackingEvaluation(
            missingRequiredMetrics.Select(item => item.Key).ToArray(),
            triggeredFlags.ToArray(),
            needsManualReview || triggeredFlags.Any(flag => flag.Key == TrackingRulesSettings.FlagManualReviewRequired),
            triggeredFlags.Any(flag => flag.BlocksApproval),
            triggeredFlags.Any(flag => flag.AdminNoteRequiredOnOverride));
    }

    public TrackingMetricState[] BuildMetricStates(Nomination nomination, TrackingRulesConfiguration rules) =>
        rules.ImportantMetrics
            .Concat(rules.OptionalMetrics)
            .Where(item => item.Enabled && item.ShowInReview)
            .Select(metric => new TrackingMetricState(
                metric.Key,
                metric.Label,
                metric.RequiredForAutoClassification,
                metric.SourceSupport,
                IsMetricPresent(metric, nomination),
                ResolveMetricValue(metric, nomination),
                metric.Description,
                metric.WindowKey,
                TrackingRulesSettings.WindowLabel(metric.WindowKey),
                TrackingRulesSettings.SupportsAutomaticWindow(metric)))
            .ToArray();

    private void ApplyEvaluation(Nomination nomination, TrackingRulesConfiguration rules, bool resetManualResolution)
    {
        var evaluation = Evaluate(nomination, rules);
        nomination.TrackingFlagsJson = TrackingRulesSettings.SerializeFlagHits(evaluation.Flags);

        if (resetManualResolution || nomination.TrackingReviewStatus is not ("reviewed" or "overridden"))
        {
            nomination.TrackingReviewStatus = evaluation.RequiresManualReview ? "flagged" : "clear";
            if (resetManualResolution)
            {
                nomination.TrackingReviewNote = null;
                nomination.TrackingReviewedByTwitchId = null;
                nomination.TrackingReviewedAt = null;
            }
        }
    }

    private static bool IsMetricPresent(TrackingMetricRuleSetting metric, Nomination nomination)
    {
        if (metric.SourceSupport == "auto" && !TrackingRulesSettings.SupportsAutomaticWindow(metric))
        {
            return false;
        }

        return metric.Key switch
        {
            TrackingRulesSettings.AvgViewers => nomination.AvgViewers.HasValue,
            TrackingRulesSettings.TrackerStatus => !string.IsNullOrWhiteSpace(nomination.TrackerStatus),
            TrackingRulesSettings.TrackerCheckedAt => nomination.TrackerCheckedAt.HasValue,
            TrackingRulesSettings.HoursStreamed => nomination.HoursStreamed.HasValue,
            TrackingRulesSettings.HoursWatched => nomination.HoursWatched.HasValue,
            TrackingRulesSettings.PeakViewers => nomination.PeakViewers.HasValue,
            TrackingRulesSettings.FollowersGained => nomination.FollowersGained.HasValue,
            _ => false,
        };
    }

    private static string ResolveMetricValue(TrackingMetricRuleSetting metric, Nomination nomination)
    {
        if (metric.SourceSupport == "auto" && !TrackingRulesSettings.SupportsAutomaticWindow(metric))
        {
            return $"Auto nur fuer {string.Join(", ", metric.AutoSupportedWindowKeys.Select(TrackingRulesSettings.WindowLabel))}";
        }

        return metric.Key switch
        {
            TrackingRulesSettings.AvgViewers => nomination.AvgViewers?.ToString() ?? "offen",
            TrackingRulesSettings.TrackerStatus => string.IsNullOrWhiteSpace(nomination.TrackerStatus) ? "offen" : nomination.TrackerStatus,
            TrackingRulesSettings.TrackerCheckedAt => nomination.TrackerCheckedAt?.ToString("g") ?? "offen",
            TrackingRulesSettings.HoursStreamed => nomination.HoursStreamed?.ToString() ?? "offen",
            TrackingRulesSettings.HoursWatched => nomination.HoursWatched?.ToString() ?? "offen",
            TrackingRulesSettings.PeakViewers => nomination.PeakViewers?.ToString() ?? "offen",
            TrackingRulesSettings.FollowersGained => nomination.FollowersGained?.ToString() ?? "offen",
            TrackingRulesSettings.CategoryFit => "Manueller Kategorie-Check",
            TrackingRulesSettings.TopCategoriesContext => BuildTopCategoriesContextSummary(metric),
            _ => "manuell",
        };
    }

    private static string BuildTopCategoriesContextSummary(TrackingMetricRuleSetting metric)
    {
        var parts = new List<string>();
        if (metric.TopCount.HasValue)
        {
            parts.Add($"Top {metric.TopCount.Value}");
        }

        if (metric.MinPrimaryCategorySharePercent.HasValue)
        {
            parts.Add($">= {metric.MinPrimaryCategorySharePercent.Value}% Hauptkategorie");
        }

        if (metric.MinPrimaryCategoryHours.HasValue)
        {
            parts.Add($">= {metric.MinPrimaryCategoryHours.Value}h Hauptkategorie");
        }

        if (metric.MaxDistinctCategoriesBeforeFlag.HasValue)
        {
            parts.Add($"Flag ab {metric.MaxDistinctCategoriesBeforeFlag.Value}+ Kategorien");
        }

        if (metric.IgnoredCategories.Length > 0)
        {
            parts.Add($"Ignore: {string.Join(", ", metric.IgnoredCategories)}");
        }

        return parts.Count > 0
            ? string.Join(" · ", parts)
            : "Top-Kategorien manuell pruefen";
    }

    private static void AddFlagIf(
        ICollection<TrackingFlagHit> target,
        IEnumerable<TrackingFlagRuleSetting> availableFlags,
        string key,
        bool shouldAdd)
    {
        if (!shouldAdd)
        {
            return;
        }

        var rule = availableFlags.FirstOrDefault(item => item.Key == key);
        if (rule is null || !rule.Enabled || !rule.AutoTriggerEnabled)
        {
            return;
        }

        target.Add(new TrackingFlagHit(
            rule.Key,
            rule.Label,
            rule.Severity,
            rule.Description,
            rule.RequiresManualReview,
            rule.BlocksApproval,
            rule.AdminNoteRequiredOnOverride));
    }
}

public sealed record NominationTrackingEvaluation(
    string[] MissingRequiredMetricKeys,
    TrackingFlagHit[] Flags,
    bool RequiresManualReview,
    bool HasBlockingFlag,
    bool RequiresOverrideNote);

public sealed record TrackingMetricState(
    string Key,
    string Label,
    bool Required,
    string SourceSupport,
    bool Present,
    string Value,
    string Description,
    string WindowKey,
    string WindowLabel,
    bool AutoWindowSupported);
