using System;

namespace PolyTrader.Modules.ResolutionFarming.Logic
{
    /// <summary>
    /// Reine, seiteneffektfreie Risiko-/Entscheidungslogik des ResolutionFarming. Bewusst aus den
    /// Services herausgezogen, damit die geldkritischen Entscheidungen (Netto-Edge nach Fees,
    /// Positionsgröße unter Cluster-/Exposure-Limits, Kill-Switch) vollständig unit-getestet werden.
    /// „Das Risikomodell IST die Strategie" (Plan): ein verlorener 95-¢-Trade vernichtet ~19 gewonnene.
    /// </summary>
    public static class FarmingRiskEngine
    {
        /// <summary>
        /// Netto-Edge pro Share in USDC: Auszahlung bei Gewinn (1.0) − Ask − Taker-Fee je Share.
        /// Die Fee liegt auf dem Notional (Ask × Shares), pro Share also Ask × bps/10000. Bei
        /// ungültigem Ask (≤0 oder ≥1) negativ, damit die Position nie als profitabel gilt.
        /// </summary>
        public static decimal NetEdge(decimal ask, int feeBps)
        {
            if (ask <= 0m || ask >= 1m) return -1m;
            decimal feePerShare = ask * feeBps / 10000m;
            return (1m - ask) - feePerShare;
        }

        /// <summary>Netto-Edge als Prozent des Einsatzes (Ask): NetEdge / Ask × 100.</summary>
        public static decimal NetEdgePct(decimal ask, int feeBps)
        {
            if (ask <= 0m || ask >= 1m) return -100m;
            return NetEdge(ask, feeBps) / ask * 100m;
        }

        /// <summary>Liegt der Netto-Edge nach Fees über der Mindestschwelle?</summary>
        public static bool HasEdge(decimal ask, int feeBps, decimal minEdgePct) =>
            NetEdgePct(ask, feeBps) >= minEdgePct;

        /// <summary>
        /// Erlaubter USDC-Einsatz für eine neue/aufstockende Position unter allen drei Limits:
        /// je Markt (absolut), je Cluster (% der Bankroll) und Gesamt-Exposure (% der Bankroll).
        /// Liefert 0, wenn irgendein Limit bereits erschöpft ist. Nie negativ.
        /// </summary>
        public static decimal AllowedPositionUsd(
            decimal maxPerMarketUsd, decimal maxPerClusterPct, decimal maxTotalExposurePct,
            decimal bankrollUsd, decimal currentTotalExposureUsd, decimal currentClusterExposureUsd,
            decimal existingMarketExposureUsd)
        {
            if (bankrollUsd <= 0m) return 0m;
            decimal marketRoom = Math.Max(0m, maxPerMarketUsd - existingMarketExposureUsd);
            decimal clusterRoom = Math.Max(0m, bankrollUsd * maxPerClusterPct / 100m - currentClusterExposureUsd);
            decimal totalRoom = Math.Max(0m, bankrollUsd * maxTotalExposurePct / 100m - currentTotalExposureUsd);
            return Math.Min(marketRoom, Math.Min(clusterRoom, totalRoom));
        }

        /// <summary>Kill-Switch: realisierter Tagesverlust erreicht/unterschreitet −Schwelle (Schwelle &gt; 0).</summary>
        public static bool ShouldKill(decimal realizedDailyPnlUsd, decimal killSwitchUsd) =>
            killSwitchUsd > 0m && realizedDailyPnlUsd <= -killSwitchUsd;

        /// <summary>Tages-Drossel erreicht (Anzahl neuer Positionen heute ≥ Limit)?</summary>
        public static bool DailyLimitReached(int newPositionsToday, int maxNewPositionsPerDay) =>
            newPositionsToday >= maxNewPositionsPerDay;
    }
}
