using System;
using System.Collections.Generic;

namespace QuantEngine.Core.Domain
{
    public class StopPriceResult
    {
        public double? StopPrice { get; set; }
        public string StopPriceStatus { get; set; } = "PASS";
        public double? Atr20Pct { get; set; }
        public double? AtrMultiplier { get; set; }
        public List<string> DataMissing { get; set; } = new List<string>();
    }

    public class StopActionLadderResult
    {
        public string Action { get; set; } = "REVIEW_HUMAN";
        public string Reason { get; set; } = "NO_FORCED_EXIT";
        public double QuantityPct { get; set; }
        public double Priority { get; set; } = 7;
    }

    public class HeatThresholdResult
    {
        public double HardBlock { get; set; }
        public double Halve { get; set; }
    }

    public static class ExitDecisions
    {
        private static bool IsValidNumber(double? value)
            => value.HasValue && !double.IsNaN(value.Value) && !double.IsInfinity(value.Value);

        public static StopPriceResult ComputeStopPriceCore(
            double? entryPrice,
            double? atr20,
            double? currentPrice = null,
            double? atrMultiplier = null)
        {
            var result = new StopPriceResult();

            if (!IsValidNumber(entryPrice))
            {
                result.StopPrice = null;
                result.StopPriceStatus = "NO_STOP_PRICE";
                result.DataMissing.Add("entry_price");
                return result;
            }

            if (!IsValidNumber(atr20) && !IsValidNumber(atrMultiplier))
            {
                result.StopPrice = entryPrice.GetValueOrDefault() * 0.92;
                result.StopPriceStatus = "DATA_MISSING — 하네스 업데이트 필요";
                result.DataMissing.Add("atr20");
                return result;
            }

            var hasCurrentPrice = IsValidNumber(currentPrice) && currentPrice!.Value != 0;

            if (!IsValidNumber(atrMultiplier) && !hasCurrentPrice)
            {
                result.StopPrice = entryPrice.GetValueOrDefault() * 0.92;
                result.StopPriceStatus = "DATA_MISSING — 하네스 업데이트 필요";
                if (!IsValidNumber(atr20)) result.DataMissing.Add("atr20");
                if (!hasCurrentPrice) result.DataMissing.Add("current_price");
                return result;
            }

            if (!IsValidNumber(atrMultiplier))
            {
                var atr20Value = atr20.GetValueOrDefault();
                var currentPriceValue = currentPrice.GetValueOrDefault();
                double atr20Pct = (atr20Value / currentPriceValue) * 100;
                atrMultiplier = atr20Pct >= 8 ? 2.0 : 1.5;
                result.Atr20Pct = atr20Pct;
            }
            else
            {
                result.Atr20Pct = hasCurrentPrice
                    ? (atr20.GetValueOrDefault() / currentPrice.GetValueOrDefault()) * 100
                    : (double?)null;
            }

            var entryPriceValue = entryPrice.GetValueOrDefault();
            var atr20FinalValue = atr20.GetValueOrDefault();
            var atrMultiplierValue = atrMultiplier.GetValueOrDefault();
            result.AtrMultiplier = atrMultiplier;
            result.StopPrice = Math.Max(entryPriceValue * 0.92, entryPriceValue - atr20FinalValue * atrMultiplierValue);
            result.StopPriceStatus = "PASS";

            return result;
        }

        public static StopActionLadderResult ComputeStopActionLadder(Dictionary<string, object> context)
        {
            string GetString(string key) => 
                context.TryGetValue(key, out var val) ? val?.ToString()?.ToUpper() ?? "" : "";

            double GetDouble(string key) =>
                context.TryGetValue(key, out var val) && double.TryParse(val?.ToString(), out var d) ? d : 0.0;

            int GetInt(string key, int def = 0) =>
                context.TryGetValue(key, out var val) && int.TryParse(val?.ToString(), out var i) ? i : def;

            bool GetBool(string key) =>
                context.TryGetValue(key, out var val) && bool.TryParse(val?.ToString(), out var b) && b;

            string timingAction = GetString("timingAction");
            if (string.IsNullOrEmpty(timingAction)) timingAction = GetString("timing_action");

            int rwPartial = GetInt("rw_partial");
            int rwPartialExcludingRw2b = GetInt("rw_partial_excluding_rw2b");

            string regimePrelim = GetString("REGIME_PRELIM");
            if (string.IsNullOrEmpty(regimePrelim)) regimePrelim = GetString("regime_prelim");

            double timingExitScore = GetDouble("timingExitScore");
            if (timingExitScore == 0) timingExitScore = GetDouble("timing_exit_score");

            double profitPct = GetDouble("profitPct");
            if (profitPct == 0) profitPct = GetDouble("profit_pct");

            int daysToTimeStop = GetInt("daysToTimeStop", 9999);
            if (daysToTimeStop == 9999) daysToTimeStop = GetInt("days_to_time_stop", 9999);

            bool trailingStopBreach = GetBool("trailingStopBreach") || GetBool("trailing_stop_breach");
            bool rw2bFastTrack = GetBool("RW2b_5d_rapid_weakness") || GetBool("rw2b_5d_rapid_weakness");

            if (timingAction == "STOP_OR_TIME_EXIT_READY" || rwPartial >= 4)
            {
                return new StopActionLadderResult
                {
                    Action = "EXIT_100",
                    Reason = timingAction == "STOP_OR_TIME_EXIT_READY" ? "STOP_OR_TIME_EXIT_READY" : "RW_EXIT_STRONG",
                    QuantityPct = 100,
                    Priority = 1
                };
            }

            if (regimePrelim == "RISK_OFF" || regimePrelim == "RISK_OFF_CANDIDATE")
            {
                return new StopActionLadderResult
                {
                    Action = "REGIME_TRIM_50",
                    Reason = regimePrelim == "RISK_OFF" ? "REGIME_RISK_OFF" : "REGIME_RISK_OFF_CANDIDATE",
                    QuantityPct = 50,
                    Priority = 2
                };
            }

            if (rw2bFastTrack && rwPartialExcludingRw2b >= 1)
            {
                return new StopActionLadderResult
                {
                    Action = "TRIM_50",
                    Reason = "RW2B_FAST_TRACK",
                    QuantityPct = 50,
                    Priority = 2.5
                };
            }

            if (rwPartial >= 3 || timingExitScore >= 75)
            {
                return new StopActionLadderResult
                {
                    Action = "TRIM_70",
                    Reason = rwPartial >= 3 ? "RW_EXIT" : "TIMING_EXIT_SCORE",
                    QuantityPct = 70,
                    Priority = 3
                };
            }

            if (trailingStopBreach || rwPartial >= 2 || (rwPartial >= 1 && timingExitScore >= 50))
            {
                return new StopActionLadderResult
                {
                    Action = "TRIM_50",
                    Reason = trailingStopBreach ? "TRAILING_STOP_BREACH" : "RW_OR_TIMING_EXIT",
                    QuantityPct = 50,
                    Priority = 4
                };
            }

            if (profitPct >= 10)
            {
                return new StopActionLadderResult
                {
                    Action = "TAKE_PROFIT_TIER1",
                    Reason = "PROFIT_PCT_THRESHOLD",
                    QuantityPct = 25,
                    Priority = 5
                };
            }

            if (daysToTimeStop <= 0)
            {
                return new StopActionLadderResult
                {
                    Action = "TIME_EXIT_100",
                    Reason = "TIME_STOP_EXPIRED",
                    QuantityPct = 100,
                    Priority = 6
                };
            }

            return new StopActionLadderResult();
        }

        public static HeatThresholdResult ComputeDynamicHeatThresholds(string regime)
        {
            string r = regime?.ToUpper() ?? "";
            if (r.Contains("EVENT_SHOCK"))
            {
                return new HeatThresholdResult { HardBlock = 5.0, Halve = 3.5 };
            }
            if (r.Contains("RISK_OFF"))
            {
                return new HeatThresholdResult { HardBlock = 7.0, Halve = 5.0 };
            }
            if (r.Contains("SECULAR_LEADER") && r.Contains("RISK_ON"))
            {
                return new HeatThresholdResult { HardBlock = 13.0, Halve = 9.0 };
            }
            if (r.Contains("RISK_ON"))
            {
                return new HeatThresholdResult { HardBlock = 12.0, Halve = 8.5 };
            }
            return new HeatThresholdResult { HardBlock = 10.0, Halve = 7.0 };
        }
    }
}
