using System;
using System.Linq;

namespace PolyTrader.Modules.ResolutionFarming.Logic
{
    /// <summary>
    /// Reine Filter-/Scoring-Logik des Markt-Scanners. Entscheidet aus Markt-Metadaten, ob ein
    /// Favorit als Kandidat taugt (Preisband, Kategorie-Whitelist, Blacklist) und leitet den
    /// Cluster-Key ab (korrelierte Favoriten dürfen nicht als unabhängige Wetten zählen).
    /// </summary>
    public static class FarmingScanner
    {
        /// <summary>Liegt der Ask im Favoriten-Preisband [min, max]?</summary>
        public static bool InPriceBand(decimal ask, decimal minPrice, decimal maxPrice) =>
            ask >= minPrice && ask <= maxPrice;

        /// <summary>
        /// Ist die Markt-Kategorie auf der Whitelist (CSV, case-insensitiv, Teil-Match in beide
        /// Richtungen)? Leere Whitelist = alles erlaubt.
        /// </summary>
        public static bool CategoryAllowed(string? category, string? whitelistCsv)
        {
            var white = SplitCsv(whitelistCsv);
            if (white.Length == 0) return true;
            string c = (category ?? string.Empty).Trim().ToLowerInvariant();
            if (c.Length == 0) return false;
            return white.Any(w => c.Contains(w) || w.Contains(c));
        }

        /// <summary>
        /// Steht der Markt (Slug oder Tags) auf der Blacklist (CSV, case-insensitiv, Teil-Match)?
        /// Leere Blacklist = nichts geblockt.
        /// </summary>
        public static bool IsBlacklisted(string? slug, string? tags, string? blacklistCsv)
        {
            var black = SplitCsv(blacklistCsv);
            if (black.Length == 0) return false;
            string hay = ((slug ?? string.Empty) + " " + (tags ?? string.Empty)).ToLowerInvariant();
            return black.Any(b => hay.Contains(b));
        }

        /// <summary>
        /// Cluster-Key aus dem Ereignis-/Series-Slug (bevorzugt), sonst dem Markt-Slug. Korrelierte
        /// Favoriten desselben Events teilen so einen Cluster (Cluster-Limits greifen). Normalisiert
        /// (trim + lower). Leer, wenn beides fehlt.
        /// </summary>
        public static string ClusterKey(string? eventSlug, string? marketSlug)
        {
            string ev = (eventSlug ?? string.Empty).Trim().ToLowerInvariant();
            if (ev.Length > 0) return ev;
            return (marketSlug ?? string.Empty).Trim().ToLowerInvariant();
        }

        /// <summary>
        /// Kandidaten-Score (höher = besser): Netto-Edge (%) gewichtet, plus ein Bonus für kürzere
        /// Kapitalbindung (frühere Auflösung). Rein für Ranking/Priorisierung, nicht für die
        /// Handelsentscheidung (die trifft <see cref="FarmingRiskEngine.HasEdge"/>).
        /// </summary>
        public static decimal Score(decimal netEdgePct, double hoursToResolution)
        {
            decimal edge = Math.Max(0m, netEdgePct);
            // Bindungsbonus: 0 h → +2, 48 h → ~0. Linear geklemmt.
            double h = Math.Max(0.0, hoursToResolution);
            decimal timeBonus = (decimal)Math.Max(0.0, (48.0 - h) / 48.0) * 2m;
            return edge + timeBonus;
        }

        private static string[] SplitCsv(string? csv) =>
            (csv ?? string.Empty)
                .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
                .Select(s => s.ToLowerInvariant())
                .Where(s => s.Length > 0)
                .ToArray();
    }
}
