using System;

namespace PolyTrader.Modules.CopyTrading.Logic
{
    /// <summary>
    /// Reine SELL-Entscheidungs-/Preislogik. Teil davon (Proportionalität) ist 1:1 aus der
    /// aktuellen Engine übernommen (Charakterisierung, damit der Phase-2-Umbau abgesichert ist);
    /// die Eskalationsleiter ist die Grundlage für Phase 0.1 (noch NICHT in der Engine verdrahtet).
    /// </summary>
    public static class SellLogic
    {
        // ----- Proportionalität (aktuelles Verhalten, Schwelle hart 30 %) -----

        /// <summary>Ab dieser Verkaufsquote des Masters kopieren wir den SELL (aktuell hart 30 %).</summary>
        public const decimal MinSignificantSellRatio = 0.30m;

        /// <summary>
        /// Anteil, den dieser SELL am Master-Bestand VOR dem Verkauf ausmacht:
        /// <c>signalSize / (masterSharesRemaining + signalSize)</c>. Denominator ≤ 0 → 0.
        /// </summary>
        public static decimal SellProportion(decimal masterSharesRemaining, decimal signalSize)
        {
            decimal total = masterSharesRemaining + signalSize;
            if (total <= 0m) return 0m;
            return signalSize / total;
        }

        // ----- Proportionaler Teilverkauf (Phase 2, pure Logik – Wiring braucht Partial-Fill) -----

        /// <summary>
        /// Wie viele unserer Shares sollen wir verkaufen, um die Verkaufsquote des Masters zu spiegeln?
        /// <para>0 = ignorieren (Verkauf unter <paramref name="minSellRatioPct"/> % = Rauschen);
        /// = <paramref name="ourShares"/> = Voll-Exit (Master ganz raus ODER Restbestand/Teil unter
        /// Polymarket-Minimum → kein Dust); sonst der proportionale Anteil.</para>
        /// </summary>
        public static decimal SharesToSell(decimal ourShares, decimal sellRatio, decimal minShares, decimal minSellRatioPct)
        {
            if (ourShares <= 0m) return 0m;
            if (sellRatio < minSellRatioPct / 100m) return 0m;   // Rauschen (Day-Trader-Zappeln)
            if (sellRatio >= 1m) return ourShares;               // Master voll raus → wir auch

            decimal target = ourShares * sellRatio;
            decimal remaining = ourShares - target;
            if (target < minShares || remaining < minShares) return ourShares; // Dust vermeiden → Voll-Exit
            return target;
        }

        // ----- Eskalationsleiter (Phase 0.1, noch nicht verdrahtet) -----

        /// <summary>
        /// Erster SELL-Limit-Preis (GTD): HF-Trader fester Abschlag von 0.005 unter dem Master-Exit,
        /// sonst prozentualer Abschlag (<paramref name="maxPriceDifferencePct"/>).
        /// </summary>
        public static decimal FirstLimit(decimal referencePrice, bool isHf, decimal maxPriceDifferencePct) =>
            isHf ? referencePrice - CopyTradingRisk.HfLimitOffset
                 : referencePrice * (1.0m - maxPriceDifferencePct / 100.0m);

        /// <summary>
        /// Absolute Untergrenze der Leiter: <c>referencePrice × (1 − SellFloorPct/100)</c>.
        /// Darunter wird nicht mehr verkauft (Position halten statt Exit-Liquidity zu werden).
        /// </summary>
        public static decimal Floor(decimal referencePrice, decimal sellFloorPct) =>
            referencePrice * (1.0m - sellFloorPct / 100.0m);

        /// <summary>
        /// Nächste Stufe nach ausbleibendem Fill: aktuellen Preis um <paramref name="stepPct"/> %
        /// relativ senken, aber nie unter den <paramref name="floor"/>.
        /// </summary>
        public static decimal NextPrice(decimal currentPrice, decimal stepPct, decimal floor) =>
            Math.Max(currentPrice * (1.0m - stepPct / 100.0m), floor);

        /// <summary>Leiter erschöpft: aktueller Preis hat den Floor erreicht/unterschritten.</summary>
        public static bool IsAtFloor(decimal currentPrice, decimal floor) => currentPrice <= floor;

        /// <summary>
        /// H4 – Preis auf den Standard-Tick (0.001) runden, identisch zur Rundung in
        /// <c>PolymarketClobClient.CalculateExactOrderAmounts</c> (AwayFromZero, 3 Dezimalen).
        /// Wird die Leiter-Order mit bereits gerundetem Preis platziert (usdc = size × roundedPrice),
        /// ergibt die interne Division exakt <c>size</c> statt ~0,1 % weniger → kein Dust-Rest, der die
        /// Leiter sonst in eine endlose Sub-Minimum-Reject-Schleife schickt.
        /// </summary>
        public static decimal RoundToTick(decimal price) => Math.Round(price, 3, MidpointRounding.AwayFromZero);

        /// <summary>
        /// Warnt vor invertierter Leiter-Konfiguration: ist der BUY-Preisabstand (%) ≥ dem SELL-Floor (%),
        /// liegt das Start-SELL-Limit (reference × (1 − maxPriceDifference/100)) auf/unter dem Floor
        /// (reference × (1 − sellFloor/100)). Die Leiter startet dann direkt am Floor → sofortige
        /// "Floor erreicht"-Benachrichtigung statt echter Eskalation. Nur der Nicht-HF-Startpfad.
        /// </summary>
        public static bool IsLadderConfigInverted(decimal maxPriceDifferencePct, decimal sellFloorPct) =>
            maxPriceDifferencePct >= sellFloorPct;

        /// <summary>Relative Schrittweite je Leiter-Stufe (%). Plan: „2 ¢ oder 3 % relativ".</summary>
        public const decimal LadderStepPct = 3.0m;

        /// <summary>
        /// Wartezeit ohne Fill, bevor eine Stufe tiefer nachgepreist wird: HF-Trader (schnelle
        /// Märkte) ~20 s, sonst ~120 s. Aus dem Plan.
        /// </summary>
        public static int LadderIntervalSeconds(bool isHf) => isHf ? 20 : 120;

        // ----- Take-Profit (Phase 0.3) -----

        /// <summary>
        /// Take-Profit-Schwelle erreicht? <c>currentPrice ≥ entryPrice × (1 + profitTargetPct/100)</c>.
        /// <paramref name="profitTargetPct"/> ≤ 0 (oder ungültiger Entry) = deaktiviert; hohe Werte
        /// (Default 9999) werden faktisch nie erreicht = inaktiv.
        /// </summary>
        public static bool IsProfitTargetReached(decimal currentPrice, decimal entryPrice, decimal profitTargetPct)
        {
            if (profitTargetPct <= 0m || entryPrice <= 0m) return false;
            return currentPrice >= entryPrice * (1.0m + profitTargetPct / 100.0m);
        }
    }
}
