using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Indicators;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Scenarios;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
using static Managing.Common.Enums;

namespace Managing.Domain.Shared.Helpers;

/// <summary>
/// Configuration for strategy combination logic
/// </summary>
public class IndicatorComboConfig
{
    /// <summary>
    /// Minimum percentage of trend strategies that must agree for strong trend (default: 66%)
    /// </summary>
    public decimal TrendStrongAgreementThreshold { get; set; } = 0.66m;

    /// <summary>
    /// Minimum percentage of signal strategies that must agree (default: 50%)
    /// </summary>
    public decimal SignalAgreementThreshold { get; set; } = 0.5m;

    /// <summary>
    /// Whether to allow signal strategies to override conflicting trends (default: true)
    /// This is useful for trend reversal signals
    /// </summary>
    public bool AllowSignalTrendOverride { get; set; } = true;

    /// <summary>
    /// Minimum confidence level to return a signal (default: Low)
    /// </summary>
    public Confidence MinimumConfidence { get; set; } = Confidence.Low;

    /// <summary>
    /// Minimum confidence level required from context strategies (default: Medium)
    /// Context strategies evaluate market conditions - higher requirements mean more conservative trading
    /// </summary>
    public Confidence MinimumContextConfidence { get; set; } = Confidence.Medium;

    /// <summary>
    /// Default exchange to use when signals don't specify one
    /// </summary>
    public TradingExchanges DefaultExchange { get; set; } = TradingExchanges.Binance;
}

public static class TradingBox
{
    private static readonly IndicatorComboConfig _defaultConfig = new();

    public static LightSignal GetSignal(HashSet<Candle> newCandles, LightScenario scenario,
        Dictionary<string, LightSignal> previousSignal, int? loopbackPeriod = 1)
    {
        return GetSignal(newCandles, scenario, previousSignal, _defaultConfig, loopbackPeriod);
    }

    public static LightSignal GetSignal(HashSet<Candle> newCandles, LightScenario lightScenario,
        Dictionary<string, LightSignal> previousSignal, IndicatorComboConfig config, int? loopbackPeriod = 1)
    {
        var signalOnCandles = new List<LightSignal>();
        var limitedCandles = newCandles.ToList().TakeLast(600).ToList();

        foreach (var indicator in lightScenario.Indicators)
        {
            IIndicator indicatorInstance = indicator.ToInterface();
            var signals = indicatorInstance.Run(newCandles);

            if (signals == null || signals.Count() == 0)
            {
                // For trend and context strategies, lack of signal might be meaningful
                // Signal strategies are expected to be sparse, so we continue
                if (indicator.SignalType == SignalType.Signal)
                {
                    continue;
                }

                // For trend strategies, no signal might mean neutral trend
                // For context strategies, no signal might mean no restrictions
                // We'll let the ComputeSignals method handle these cases
                continue;
            }

            // Ensure limitedCandles is ordered chronologically
            var orderedCandles = limitedCandles.OrderBy(c => c.Date).ToList();
            var loopback = loopbackPeriod.HasValue && loopbackPeriod > 1 ? loopbackPeriod.Value : 1;
            var candleLoopback = orderedCandles.TakeLast(loopback).ToList();

            if (!candleLoopback.Any())
            {
                // Handle empty case (e.g., log warning, skip processing)
                continue;
            }

            var loopbackStartDate = candleLoopback.First().Date;

            foreach (var signal in signals.Where(s => s.Date >= loopbackStartDate))
            {
                var hasExistingSignal = previousSignal.ContainsKey(signal.Identifier);
                if (!hasExistingSignal)
                {
                    bool shouldAdd = previousSignal.Count == 0 || previousSignal.Values.Last().Date < signal.Date;
                    if (shouldAdd)
                    {
                        signalOnCandles.Add(signal);
                    }
                }
            }
        }

        // Keep only the latest signal per indicator to avoid count mismatch
        var latestSignalsPerIndicator = signalOnCandles
            .GroupBy(s => s.IndicatorName)
            .Select(g => g.OrderByDescending(s => s.Date).First())
            .ToHashSet();

        // Remove the restrictive requirement that ALL strategies must produce signals
        // Instead, let ComputeSignals handle the logic based on what we have
        if (!latestSignalsPerIndicator.Any())
        {
            return null; // No signals from any strategy
        }

        var data = newCandles.First();
        return ComputeSignals(lightScenario, latestSignalsPerIndicator, MiscExtensions.ParseEnum<Ticker>(data.Ticker),
            data.Timeframe, config);
    }

    public static LightSignal ComputeSignals(LightScenario scenario, HashSet<LightSignal> signalOnCandles,
        Ticker ticker,
        Timeframe timeframe)
    {
        return ComputeSignals(scenario, signalOnCandles, ticker, timeframe, _defaultConfig);
    }

    public static LightSignal ComputeSignals(LightScenario scenario, HashSet<LightSignal> signalOnCandles,
        Ticker ticker,
        Timeframe timeframe, IndicatorComboConfig config)
    {
        if (scenario.Indicators.Count == 1)
        {
            // Only one strategy, return the single signal
            return signalOnCandles.Single();
        }

        signalOnCandles = signalOnCandles.OrderBy(s => s.Date).ToHashSet();

        // Check if all strategies produced signals - this is required for composite signals
        var strategyNames = scenario.Indicators.Select(s => s.Name).ToHashSet();
        var signalIndicatorNames = signalOnCandles.Select(s => s.IndicatorName).ToHashSet();

        if (!strategyNames.SetEquals(signalIndicatorNames))
        {
            // Not all strategies produced signals - composite signal requires all strategies to contribute
            return null;
        }

        // Group signals by type for analysis
        var signals = signalOnCandles.Where(s => s.SignalType == SignalType.Signal).ToList();
        var trendSignals = signalOnCandles.Where(s => s.SignalType == SignalType.Trend).ToList();
        var contextSignals = signalOnCandles.Where(s => s.SignalType == SignalType.Context).ToList();

        // Context validation - evaluates market conditions based on confidence levels
        if (!ValidateContextStrategies(scenario, contextSignals, config))
        {
            return null; // Context strategies are blocking the trade
        }

        // Check for 100% agreement across ALL signals (no threshold voting)
        var allDirectionalSignals = signalOnCandles
            .Where(s => s.Direction != TradeDirection.None && s.SignalType != SignalType.Context).ToList();

        if (!allDirectionalSignals.Any())
        {
            return null; // No directional signals available
        }

        // Require 100% agreement - all signals must have the same direction
        var lastSignalDirection = allDirectionalSignals.Last().Direction;
        if (!allDirectionalSignals.All(s => s.Direction == lastSignalDirection))
        {
            return null; // Signals are not in complete agreement
        }

        var finalDirection = lastSignalDirection;

        // Calculate confidence based on the average confidence of all signals
        var averageConfidence = CalculateAverageConfidence(allDirectionalSignals);

        if (finalDirection == TradeDirection.None || averageConfidence < config.MinimumConfidence)
        {
            return null; // No valid signal or below minimum confidence
        }

        // Create composite signal
        var lastSignal = signals.LastOrDefault() ??
                         trendSignals.LastOrDefault() ?? contextSignals.LastOrDefault();

        return new LightSignal(
            ticker,
            finalDirection,
            averageConfidence,
            lastSignal?.Candle,
            lastSignal?.Date ?? DateTime.UtcNow,
            lastSignal?.Exchange ?? config.DefaultExchange,
            IndicatorType.Composite,
            SignalType.Signal, "Aggregated");
    }

    /// <summary>
    /// Calculates the average confidence level from a list of signals
    /// </summary>
    private static Confidence CalculateAverageConfidence(List<LightSignal> signals)
    {
        if (!signals.Any())
        {
            return Confidence.None;
        }

        // Convert confidence enum to numeric values for averaging
        var confidenceValues = signals.Select(s => (int)s.Confidence).ToList();
        var averageValue = confidenceValues.Average();

        // Round to nearest confidence level
        var roundedValue = Math.Round(averageValue);

        // Ensure the value is within valid confidence enum range
        roundedValue = Math.Max(0, Math.Min(3, roundedValue));

        return (Confidence)(int)roundedValue;
    }

    /// <summary>
    /// Validates context strategies based on confidence levels indicating market condition quality
    /// </summary>
    private static bool ValidateContextStrategies(LightScenario scenario, List<LightSignal> contextSignals,
        IndicatorComboConfig config)
    {
        var contextStrategiesCount = scenario.Indicators.Count(s => s.SignalType == SignalType.Context);

        if (contextStrategiesCount == 0)
        {
            return true; // No context strategies, no restrictions
        }

        // Check if we have signals from all context strategies
        if (contextSignals.Count != contextStrategiesCount)
        {
            return false; // Missing context information
        }

        // All context strategies must meet minimum confidence requirements
        // This ensures market conditions are suitable for trading
        return contextSignals.All(s => s.Confidence >= config.MinimumContextConfidence);
    }

    /// <summary>
    /// Evaluates trend direction using majority voting with neutral handling
    /// </summary>
    private static TradeDirection EvaluateTrendDirection(List<Signal> trendSignals, IndicatorComboConfig config)
    {
        if (!trendSignals.Any())
        {
            return TradeDirection.None; // No trend information available
        }

        var longCount = trendSignals.Count(s => s.Direction == TradeDirection.Long);
        var shortCount = trendSignals.Count(s => s.Direction == TradeDirection.Short);
        var neutralCount = trendSignals.Count(s => s.Direction == TradeDirection.None);

        // Strong trend agreement using configurable threshold
        var totalTrend = trendSignals.Count;
        if (longCount > totalTrend * config.TrendStrongAgreementThreshold)
            return TradeDirection.Long;
        if (shortCount > totalTrend * config.TrendStrongAgreementThreshold)
            return TradeDirection.Short;

        // Moderate trend agreement (> 50% but <= strong threshold)
        if (longCount > shortCount && longCount > neutralCount)
            return TradeDirection.Long;
        if (shortCount > longCount && shortCount > neutralCount)
            return TradeDirection.Short;

        // No clear trend or too many neutrals
        return TradeDirection.None;
    }

    /// <summary>
    /// Evaluates signal direction using weighted majority voting
    /// </summary>
    private static TradeDirection EvaluateSignalDirection(List<Signal> signalStrategies, IndicatorComboConfig config)
    {
        if (!signalStrategies.Any())
        {
            return TradeDirection.None; // No signal strategies
        }

        // For signal strategies, we need stronger agreement since they're rare and should be precise
        var longCount = signalStrategies.Count(s => s.Direction == TradeDirection.Long);
        var shortCount = signalStrategies.Count(s => s.Direction == TradeDirection.Short);

        // Use configurable agreement threshold for signals
        var totalSignals = signalStrategies.Count;
        if (longCount > totalSignals * config.SignalAgreementThreshold)
            return TradeDirection.Long;
        if (shortCount > totalSignals * config.SignalAgreementThreshold)
            return TradeDirection.Short;

        return TradeDirection.None;
    }

    /// <summary>
    /// Determines final signal direction and confidence based on signal and trend analysis
    /// </summary>
    private static (TradeDirection Direction, Confidence Confidence) DetermineFinalSignal(
        TradeDirection signalDirection,
        TradeDirection trendDirection,
        List<Signal> signalStrategies,
        List<Signal> trendStrategies,
        IndicatorComboConfig config)
    {
        // Priority 1: If we have signal strategies, they take precedence
        if (signalDirection != TradeDirection.None)
        {
            // Signal strategies have fired - check if trend supports or conflicts
            if (trendDirection == signalDirection)
            {
                // Perfect alignment - signal and trend agree
                return (signalDirection, Confidence.High);
            }
            else if (trendDirection == TradeDirection.None)
            {
                // No trend information or neutral trend - medium confidence
                return (signalDirection, Confidence.Medium);
            }
            else if (config.AllowSignalTrendOverride)
            {
                // Trend conflicts with signal but we allow override
                // This could be a trend reversal signal
                return (signalDirection, Confidence.Low);
            }
            else
            {
                // Trend conflicts and we don't allow override
                return (TradeDirection.None, Confidence.None);
            }
        }

        // Priority 2: Only trend strategies available
        if (trendDirection != TradeDirection.None)
        {
            // Calculate confidence based on trend strength
            var totalTrend = trendStrategies.Count;
            var majorityDirection = trendDirection == TradeDirection.Long
                ? trendStrategies.Count(s => s.Direction == TradeDirection.Long)
                : trendStrategies.Count(s => s.Direction == TradeDirection.Short);

            var agreementPercentage = (decimal)majorityDirection / totalTrend;

            if (agreementPercentage >= 0.8m)
                return (trendDirection, Confidence.High);
            else if (agreementPercentage >= config.TrendStrongAgreementThreshold)
                return (trendDirection, Confidence.Medium);
            else
                return (trendDirection, Confidence.Low);
        }

        // No valid signal found
        return (TradeDirection.None, Confidence.None);
    }

    public static MoneyManagement GetBestMoneyManagement(List<Candle> candles, List<Position> positions,
        MoneyManagement originMoneyManagement)
    {
        // Foreach positions, identitify the price when the position is open
        // Then, foreach candles, get the maximum price before the next position
        // Then, identify the lowest price before the maximum price 
        // Base on that, return the best StopLoss and TakeProfit to use and build a 
        var moneyManagement = new MoneyManagement();
        var stoplossPercentage = new List<decimal>();
        var takeProfitsPercentage = new List<decimal>();

        if (positions.Count == 0)
            return null;

        for (var i = 0; i < positions.Count; i++)
        {
            var position = positions[i];
            var nextPosition = i + 1 < positions.Count ? positions[i + 1] : null;
            var (stopLoss, takeProfit) = GetBestSltpForPosition(candles, position, nextPosition);

            stoplossPercentage.Add(stopLoss);
            takeProfitsPercentage.Add(takeProfit);
        }

        moneyManagement.StopLoss = stoplossPercentage.Average();
        moneyManagement.TakeProfit = takeProfitsPercentage.Average();
        moneyManagement.Timeframe = originMoneyManagement.Timeframe;
        moneyManagement.Leverage = originMoneyManagement.Leverage;
        moneyManagement.Name = "Optimized";
        return moneyManagement;
    }

    public static (decimal Stoploss, decimal TakeProfit) GetBestSltpForPosition(List<Candle> candles, Position position,
        Position nextPosition)
    {
        var stopLoss = 0M;
        var takeProfit = 0M;
        var candlesBeforeNextPosition = candles.Where(c =>
                c.Date >= position.Date && c.Date <= (nextPosition == null ? candles.Last().Date : nextPosition.Date))
            .ToList();

        if (position.OriginDirection == TradeDirection.Long)
        {
            var maxPrice = candlesBeforeNextPosition.Max(c => c.High);
            var minPrice = candlesBeforeNextPosition.TakeWhile(c => c.High <= maxPrice).Min(c => c.Low);
            stopLoss = GetPercentageFromEntry(position.Open.Price, minPrice);
            takeProfit = GetPercentageFromEntry(position.Open.Price, maxPrice);
        }
        else if (position.OriginDirection == TradeDirection.Short)
        {
            var minPrice = candlesBeforeNextPosition.Min(c => c.Low);
            var maxPrice = candlesBeforeNextPosition.TakeWhile(c => c.Low >= minPrice).Max(c => c.High);
            stopLoss = GetPercentageFromEntry(position.Open.Price, maxPrice);
            takeProfit = GetPercentageFromEntry(position.Open.Price, minPrice);
        }

        return (stopLoss, takeProfit);
    }

    private static decimal GetPercentageFromEntry(decimal entry, decimal price)
    {
        return Math.Abs(100 - ((100 * price) / entry));
    }

    public static ProfitAndLoss GetProfitAndLoss(Position position, decimal quantity, decimal price, decimal leverage)
    {
        var orders = new List<Tuple<decimal, decimal>>
        {
            new Tuple<decimal, decimal>(position.Open.Quantity, position.Open.Price),
            new Tuple<decimal, decimal>(-quantity, price)
        };


        var pnl = new ProfitAndLoss(orders, position.OriginDirection);
        // Apply leverage on the realized pnl
        pnl.Realized = pnl.Realized * leverage;
        return pnl;
    }

    /// <summary>
    /// Calculates the total volume traded across all positions
    /// </summary>
    /// <param name="positions">List of positions to analyze</param>
    /// <returns>The total volume traded in decimal</returns>
    public static decimal GetTotalVolumeTraded(Dictionary<Guid, Position> positions)
    {
        decimal totalVolume = 0;

        foreach (var position in positions.Values)
        {
            // Add entry volume
            totalVolume += position.Open.Quantity * position.Open.Price;

            // Add exit volumes from stop loss or take profits if they were executed
            if (position.StopLoss.Status == TradeStatus.Filled)
            {
                totalVolume += position.StopLoss.Quantity * position.StopLoss.Price;
            }

            if (position.TakeProfit1.Status == TradeStatus.Filled)
            {
                totalVolume += position.TakeProfit1.Quantity * position.TakeProfit1.Price;
            }

            if (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled)
            {
                totalVolume += position.TakeProfit2.Quantity * position.TakeProfit2.Price;
            }
        }

        return totalVolume;
    }

    /// <summary>
    /// Calculates the volume traded in the last 24 hours
    /// </summary>
    /// <param name="positions">List of positions to analyze</param>
    /// <returns>The volume traded in the last 24 hours in decimal</returns>
    public static decimal GetLast24HVolumeTraded(Dictionary<Guid, Position> positions)
    {
        decimal last24hVolume = 0;
        DateTime cutoff = DateTime.UtcNow.AddHours(-24);

        foreach (var position in positions.Values)
        {
            // Check if any part of this position was traded in the last 24 hours

            // Add entry volume if it was within the last 24 hours
            if (position.Open.Date >= cutoff)
            {
                last24hVolume += position.Open.Quantity * position.Open.Price;
            }

            // Add exit volumes if they were executed within the last 24 hours
            if (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date >= cutoff)
            {
                last24hVolume += position.StopLoss.Quantity * position.StopLoss.Price;
            }

            if (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date >= cutoff)
            {
                last24hVolume += position.TakeProfit1.Quantity * position.TakeProfit1.Price;
            }

            if (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled &&
                position.TakeProfit2.Date >= cutoff)
            {
                last24hVolume += position.TakeProfit2.Quantity * position.TakeProfit2.Price;
            }
        }

        return last24hVolume;
    }

    /// <summary>
    /// Gets the win/loss counts from positions
    /// </summary>
    /// <param name="positions">List of positions to analyze</param>
    /// <returns>A tuple containing (wins, losses)</returns>
    public static (int Wins, int Losses) GetWinLossCount(Dictionary<Guid, Position> positions)
    {
        int wins = 0;
        int losses = 0;

        foreach (var position in positions.Values)
        {
            if (position.ProfitAndLoss != null && position.ProfitAndLoss.Realized > 0)
            {
                wins++;
            }
            else
            {
                losses++;
            }
        }

        return (wins, losses);
    }

    /// <summary>
    /// Calculates the ROI for the last 24 hours
    /// </summary>
    /// <param name="positions">List of positions to analyze</param>
    /// <returns>The ROI for the last 24 hours as a percentage</returns>
    public static decimal GetLast24HROI(Dictionary<Guid, Position> positions)
    {
        decimal profitLast24h = 0;
        decimal investmentLast24h = 0;
        DateTime cutoff = DateTime.UtcNow.AddHours(-24);

        foreach (var position in positions.Values)
        {
            // Only count positions that were opened or closed within the last 24 hours
            if (position.IsFinished() &&
                (position.Open.Date >= cutoff ||
                 (position.StopLoss.Status == TradeStatus.Filled && position.StopLoss.Date >= cutoff) ||
                 (position.TakeProfit1.Status == TradeStatus.Filled && position.TakeProfit1.Date >= cutoff) ||
                 (position.TakeProfit2 != null && position.TakeProfit2.Status == TradeStatus.Filled &&
                  position.TakeProfit2.Date >= cutoff)))
            {
                profitLast24h += position.ProfitAndLoss != null ? position.ProfitAndLoss.Realized : 0;
                investmentLast24h += position.Open.Quantity * position.Open.Price;
            }
        }

        // Avoid division by zero
        if (investmentLast24h == 0)
            return 0;

        return (profitLast24h / investmentLast24h) * 100;
    }

    /// <summary>
    /// Calculates profit and loss for positions within a specific time range
    /// </summary>
    /// <param name="positions">List of positions to analyze</param>
    /// <param name="timeFilter">Time filter to apply (24H, 3D, 1W, 1M, 1Y, Total)</param>
    /// <returns>The PnL for positions in the specified range</returns>
    public static decimal GetPnLInTimeRange(List<Position> positions, string timeFilter)
    {
        // If Total, just return the total PnL
        if (timeFilter == "Total")
        {
            return positions
                .Where(p => p.IsFinished() && p.ProfitAndLoss != null)
                .Sum(p => p.ProfitAndLoss.Realized);
        }

        // Convert time filter to a DateTime
        DateTime cutoffDate = DateTime.UtcNow;

        switch (timeFilter)
        {
            case "24H":
                cutoffDate = DateTime.UtcNow.AddHours(-24);
                break;
            case "3D":
                cutoffDate = DateTime.UtcNow.AddDays(-3);
                break;
            case "1W":
                cutoffDate = DateTime.UtcNow.AddDays(-7);
                break;
            case "1M":
                cutoffDate = DateTime.UtcNow.AddMonths(-1);
                break;
            case "1Y":
                cutoffDate = DateTime.UtcNow.AddYears(-1);
                break;
        }

        // Include positions that were closed within the time range
        return positions
            .Where(p => p.IsFinished() && p.ProfitAndLoss != null &&
                        (p.Date >= cutoffDate ||
                         (p.StopLoss.Status == TradeStatus.Filled && p.StopLoss.Date >= cutoffDate) ||
                         (p.TakeProfit1.Status == TradeStatus.Filled && p.TakeProfit1.Date >= cutoffDate) ||
                         (p.TakeProfit2 != null && p.TakeProfit2.Status == TradeStatus.Filled &&
                          p.TakeProfit2.Date >= cutoffDate)))
            .Sum(p => p.ProfitAndLoss.Realized);
    }

    /// <summary>
    /// Calculates ROI for positions within a specific time range
    /// </summary>
    /// <param name="positions">List of positions to analyze</param>
    /// <param name="timeFilter">Time filter to apply (24H, 3D, 1W, 1M, 1Y, Total)</param>
    /// <returns>The ROI as a percentage for positions in the specified range</returns>
    public static decimal GetROIInTimeRange(List<Position> positions, string timeFilter)
    {
        // If no positions, return 0
        if (!positions.Any())
        {
            return 0;
        }

        // Convert time filter to a DateTime
        DateTime cutoffDate = DateTime.UtcNow;

        if (timeFilter != "Total")
        {
            switch (timeFilter)
            {
                case "24H":
                    cutoffDate = DateTime.UtcNow.AddHours(-24);
                    break;
                case "3D":
                    cutoffDate = DateTime.UtcNow.AddDays(-3);
                    break;
                case "1W":
                    cutoffDate = DateTime.UtcNow.AddDays(-7);
                    break;
                case "1M":
                    cutoffDate = DateTime.UtcNow.AddMonths(-1);
                    break;
                case "1Y":
                    cutoffDate = DateTime.UtcNow.AddYears(-1);
                    break;
            }
        }

        // Filter positions in the time range
        var filteredPositions = timeFilter == "Total"
            ? positions.Where(p => p.IsFinished() && p.ProfitAndLoss != null)
            : positions.Where(p => p.IsFinished() && p.ProfitAndLoss != null &&
                                   (p.Date >= cutoffDate ||
                                    (p.StopLoss.Status == TradeStatus.Filled && p.StopLoss.Date >= cutoffDate) ||
                                    (p.TakeProfit1.Status == TradeStatus.Filled && p.TakeProfit1.Date >= cutoffDate) ||
                                    (p.TakeProfit2 != null && p.TakeProfit2.Status == TradeStatus.Filled &&
                                     p.TakeProfit2.Date >= cutoffDate)));

        // Calculate investment and profit
        decimal totalInvestment = filteredPositions.Sum(p => p.Open.Quantity * p.Open.Price);
        decimal totalProfit = filteredPositions.Sum(p => p.ProfitAndLoss.Realized);

        // Calculate ROI
        if (totalInvestment == 0)
        {
            return 0;
        }

        return (totalProfit / totalInvestment) * 100;
    }

    /// <summary>
    /// Gets the win/loss counts from positions in a specific time range
    /// </summary>
    /// <param name="positions">List of positions to analyze</param>
    /// <param name="timeFilter">Time filter to apply (24H, 3D, 1W, 1M, 1Y, Total)</param>
    /// <returns>A tuple containing (wins, losses)</returns>
    public static (int Wins, int Losses) GetWinLossCountInTimeRange(List<Position> positions, string timeFilter)
    {
        // Convert time filter to a DateTime
        DateTime cutoffDate = DateTime.UtcNow;

        if (timeFilter != "Total")
        {
            switch (timeFilter)
            {
                case "24H":
                    cutoffDate = DateTime.UtcNow.AddHours(-24);
                    break;
                case "3D":
                    cutoffDate = DateTime.UtcNow.AddDays(-3);
                    break;
                case "1W":
                    cutoffDate = DateTime.UtcNow.AddDays(-7);
                    break;
                case "1M":
                    cutoffDate = DateTime.UtcNow.AddMonths(-1);
                    break;
                case "1Y":
                    cutoffDate = DateTime.UtcNow.AddYears(-1);
                    break;
            }
        }

        // Filter positions in the time range
        var filteredPositions = timeFilter == "Total"
            ? positions.Where(p => p.IsFinished())
            : positions.Where(p => p.IsFinished() &&
                                   (p.Date >= cutoffDate ||
                                    (p.StopLoss.Status == TradeStatus.Filled && p.StopLoss.Date >= cutoffDate) ||
                                    (p.TakeProfit1.Status == TradeStatus.Filled && p.TakeProfit1.Date >= cutoffDate) ||
                                    (p.TakeProfit2 != null && p.TakeProfit2.Status == TradeStatus.Filled &&
                                     p.TakeProfit2.Date >= cutoffDate)));

        int wins = 0;
        int losses = 0;

        foreach (var position in filteredPositions)
        {
            if (position.ProfitAndLoss != null && position.ProfitAndLoss.Realized > 0)
            {
                wins++;
            }
            else
            {
                losses++;
            }
        }

        return (wins, losses);
    }
}