using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands;
using Managing.Application.Trading.Handlers;
using Managing.Common;
using Managing.Core;
using Managing.Domain.Accounts;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.Indicators;
using Managing.Domain.Scenarios;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Strategies;
using Managing.Domain.Synth.Models;
using Managing.Domain.Trades;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using static Managing.Common.Enums;

namespace Managing.Application.Bots;

public class TradingBotBase : ITradingBot
{
    public readonly ILogger<TradingBotBase> Logger;
    private readonly IServiceScopeFactory _scopeFactory;

    public TradingBotConfig Config { get; set; }
    public Account Account { get; set; }
    public Dictionary<string, LightSignal> Signals { get; set; }
    public Dictionary<Guid, Position> Positions { get; set; }
    public Dictionary<DateTime, decimal> WalletBalances { get; set; }
    public DateTime PreloadSince { get; set; }
    public int PreloadedCandlesCount { get; set; }
    public long ExecutionCount { get; set; } = 0;
    public Guid Identifier { get; set; } = Guid.Empty;
    public Candle LastCandle { get; set; }
    public DateTime? LastPositionClosingTime { get; set; }


    public TradingBotBase(
        ILogger<TradingBotBase> logger,
        IServiceScopeFactory scopeFactory,
        TradingBotConfig config
    )
    {
        _scopeFactory = scopeFactory;
        Logger = logger;
        Config = config;
        Signals = new Dictionary<string, LightSignal>();
        Positions = new Dictionary<Guid, Position>();
        WalletBalances = new Dictionary<DateTime, decimal>();
        PreloadSince = CandleExtensions.GetBotPreloadSinceFromTimeframe(config.Timeframe);
    }

    public async Task Start(BotStatus previousStatus)
    {
        if (!Config.IsForBacktest)
        {
            // Start async initialization in the background without blocking
            try
            {
                // Load account asynchronously
                await LoadAccount();

                // Load last candle asynchronously
                await LoadLastCandle();

                if (Account == null)
                {
                    await LogWarning($"Account {Config.AccountName} not found. Bot cannot start.");
                    throw new ArgumentException("Account not found");
                }

                // Cancel orders
                // await CancelAllOrders();

                // Send startup message only for fresh starts (not reboots)
                switch (previousStatus)
                {
                    case BotStatus.Saved:
                        var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
                        var startupMessage = $"🚀 **Bot Started Successfully!**\n\n" +
                                             $"📊 **Trading Setup:**\n" +
                                             $"🎯 Ticker: `{Config.Ticker}`\n" +
                                             $"⏰ Timeframe: `{Config.Timeframe}`\n" +
                                             $"🎮 Scenario: `{Config.Scenario?.Name ?? "Unknown"}`\n" +
                                             $"💰 Balance: `${Config.BotTradingBalance:F2}`\n" +
                                             $"👀 Mode: `{(Config.IsForWatchingOnly ? "Watch Only" : "Live Trading")}`\n\n" +
                                             $"📈 **Active Indicators:** `{string.Join(", ", indicatorNames)}`\n\n" +
                                             $"✅ Ready to monitor signals and execute trades!\n" +
                                             $"📢 I'll notify you when signals are triggered.";

                        await LogInformation(startupMessage);
                        break;

                    case BotStatus.Running:
                        return;

                    case BotStatus.Stopped:
                        // If status was Stopped we log a message to inform the user that the bot is restarting
                        await LogInformation($"🔄 **Bot Restarted**\n" +
                                             $"📊 Resuming operations with {Signals.Count} signals and {Positions.Count} positions\n" +
                                             $"✅ Ready to continue trading");
                        break;

                    default:
                        // Handle any other status if needed
                        break;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error during bot startup: {Message}", ex.Message);
            }
        }
    }

    public async Task LoadLastCandle()
    {
        await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
        {
            var candles = await exchangeService.GetCandlesInflux(Account.Exchange, Config.Ticker, PreloadSince,
                Config.Timeframe, DateTime.UtcNow, 2);
            LastCandle = candles.Last();
        });
    }

    public async Task LoadAccount()
    {
        if (Config.IsForBacktest) return;
        await ServiceScopeHelpers.WithScopedService<IAccountService>(_scopeFactory, async accountService =>
        {
            var account = await accountService.GetAccountByAccountName(Config.AccountName, false, false);
            Account = account;
        });
    }

    public async Task Run()
    {
        if (!Config.IsForBacktest)
        {
            await LoadLastCandle();
        }

        // Update signals for live trading only
        if (!Config.IsForBacktest)
        {
            await UpdateSignals();
        }

        if (!Config.IsForWatchingOnly)
            await ManagePositions();

        UpdateWalletBalances();
        if (!Config.IsForBacktest)
        {
            ExecutionCount++;

            Logger.LogInformation(
                "Bot Status - ServerDate: {ServerDate}, LastCandleDate: {LastCandleDate}, Signals: {SignalCount}, Executions: {ExecutionCount}, Positions: {PositionCount}",
                DateTime.UtcNow, LastCandle.Date, Signals.Count, ExecutionCount, Positions.Count);
        }
    }

    public async Task UpdateSignals(HashSet<Candle>? candles = null)
    {
        // If position open and not flipped, do not update signals
        if (!Config.FlipPosition && Positions.Any(p => !p.Value.IsFinished())) return;

        // Check if we're in cooldown period for any direction
        if (IsInCooldownPeriod())
        {
            // Still in cooldown period, skip signal generation
            return;
        }

        if (Config.IsForBacktest && candles != null)
        {
            var backtestSignal =
                TradingBox.GetSignal(candles, Config.Scenario, Signals, Config.Scenario.LoopbackPeriod);
            if (backtestSignal == null) return;
            await AddSignal(backtestSignal);
        }
        else
        {
            await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
            {
                var scenarioRunnerGrain = grainFactory.GetGrain<IScenarioRunnerGrain>(Guid.NewGuid());
                var signal = await scenarioRunnerGrain.GetSignals(Config, Signals, PreloadSince, LastCandle);
                if (signal == null) return;
                await AddSignal(signal);
            });
        }
    }

    private async Task<LightSignal> RecreateSignalFromPosition(Position position)
    {
        try
        {
            // Create a dummy candle for the position opening time
            var positionCandle = new Candle
            {
                Date = position.Open.Date,
                OpenTime = position.Open.Date,
                Open = position.Open.Price,
                Close = position.Open.Price,
                High = position.Open.Price,
                Low = position.Open.Price,
                Volume = 0,
                Exchange = TradingExchanges.Evm,
                Ticker = Config.Ticker.ToString(),
                Timeframe = Config.Timeframe
            };

            // Create a new signal based on position information
            var recreatedSignal = new LightSignal(
                ticker: Config.Ticker,
                direction: position.OriginDirection,
                confidence: Confidence.Medium, // Default confidence for recreated signals
                candle: positionCandle,
                date: position.Open.Date,
                exchange: TradingExchanges.Evm,
                indicatorType: IndicatorType.Stc, // Use a valid strategy type for recreated signals
                signalType: SignalType.Signal,
                indicatorName: "RecreatedSignal"
            );

            // Since Signal identifier is auto-generated, we need to update our position 
            // to use the new signal identifier, or find another approach
            // For now, let's update the position's SignalIdentifier to match the recreated signal
            position.SignalIdentifier = recreatedSignal.Identifier;

            recreatedSignal.Status = SignalStatus.PositionOpen;

            // Add the recreated signal to our collection
            Signals.Add(recreatedSignal.Identifier, recreatedSignal);

            await LogInformation(
                $"🔍 **Signal Recovery Success**\nRecreated signal: `{recreatedSignal.Identifier}`\nFor position: `{position.Identifier}`");
            return recreatedSignal;
        }
        catch (Exception ex)
        {
            await LogWarning($"Error recreating signal for position {position.Identifier}: {ex.Message}");
            return null;
        }
    }

    private async Task ManagePositions()
    {
        // First, process all existing positions that are not finished
        foreach (var position in Positions.Values.Where(p => !p.IsFinished()))
        {
            var signalForPosition = Signals[position.SignalIdentifier];
            if (signalForPosition == null)
            {
                await LogInformation(
                    $"🔍 **Signal Recovery**\nSignal not found for position `{position.Identifier}`\nRecreating signal from position data...");

                // Recreate the signal based on position information
                signalForPosition = await RecreateSignalFromPosition(position);

                if (signalForPosition == null)
                {
                    await LogWarning($"Failed to recreate signal for position {position.Identifier}");
                    continue;
                }
            }

            // Ensure signal status is correctly set to PositionOpen if position is not finished
            if (signalForPosition.Status != SignalStatus.PositionOpen && position.Status != PositionStatus.Finished)
            {
                await LogInformation(
                    $"🔄 **Signal Status Update**\nSignal: `{signalForPosition.Identifier}`\nStatus: `{signalForPosition.Status}` → `PositionOpen`");
                SetSignalStatus(signalForPosition.Identifier, SignalStatus.PositionOpen);
            }

            await UpdatePosition(signalForPosition, position);
        }

        // Then, open positions for signals waiting for a position open
        // But first, check if we already have a position for any of these signals
        var signalsWaitingForPosition = Signals.Values.Where(s => s.Status == SignalStatus.WaitingForPosition);

        foreach (var signal in signalsWaitingForPosition)
        {
            if (signal.Date < LastCandle.Date)
            {
                await LogWarning(
                    $"❌ **Signal Expired**\nSignal `{signal.Identifier}` is older than last candle `{LastCandle.Date}`\nStatus: `Expired`");
                SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                continue;
            }

            // Check if we already have a position for this signal (in case it was added but not processed yet)
            var existingPosition = Positions.Values.FirstOrDefault(p => p.SignalIdentifier == signal.Identifier);

            if (existingPosition != null)
            {
                // Position already exists for this signal, update signal status
                await LogInformation(
                    $"🔄 **Signal Status Update**\nSignal: `{signal.Identifier}`\nStatus: `{signal.Status}` → `PositionOpen`\nPosition already exists: `{existingPosition.Identifier}`");
                SetSignalStatus(signal.Identifier, SignalStatus.PositionOpen);
                continue;
            }

            // No existing position found, proceed to open a new one
            var newlyCreatedPosition = await OpenPosition(signal);

            if (newlyCreatedPosition != null)
            {
                Positions[newlyCreatedPosition.Identifier] = newlyCreatedPosition;
            }
            else
            {
                await LogWarning(
                    $"⚠️ **Position Creation Failed**\nSignal: `{signal.Identifier}`\nPosition creation returned null");
            }
        }
    }

    private void UpdateWalletBalances()
    {
        var date = DateTime.UtcNow;

        if (WalletBalances.Count == 0)
        {
            WalletBalances[date] = Config.BotTradingBalance;
            return;
        }

        if (!WalletBalances.ContainsKey(date))
        {
            var previousBalance = WalletBalances.First().Value;
            WalletBalances[date] = previousBalance + GetProfitAndLoss();
        }
    }

    private async Task UpdatePosition(LightSignal signal, Position positionForSignal)
    {
        try
        {
            Position position = null;
            List<Position> positionsExchange = null;
            await ServiceScopeHelpers.WithScopedService<ITradingService>(_scopeFactory, async tradingService =>
            {
                position = Config.IsForBacktest
                    ? positionForSignal
                    : await tradingService.GetPositionByIdentifierAsync(positionForSignal.Identifier);

                if (Config.IsForBacktest)
                {
                    positionsExchange = new List<Position> { position };
                }
                else
                {
                    await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                        async exchangeService =>
                        {
                            positionsExchange = (await exchangeService.GetBrokerPositions(Account)).ToList();
                        });
                }
            });

            if (!Config.IsForBacktest)
            {
                var brokerPosition = positionsExchange.FirstOrDefault(p => p.Ticker == Config.Ticker);
                if (brokerPosition != null)
                {
                    UpdatePositionPnl(positionForSignal.Identifier, brokerPosition.ProfitAndLoss.Realized);

                    if (position.Status.Equals(PositionStatus.New))
                    {
                        await SetPositionStatus(position.SignalIdentifier, PositionStatus.Filled);

                        // Notify platform summary about the executed trade
                        try
                        {
                            await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory,
                                async grainFactory =>
                                {
                                    var platformGrain =
                                        grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");
                                    var tradeExecutedEvent = new TradeExecutedEvent
                                    {
                                        TradeId = position.Identifier,
                                        Ticker = position.Ticker,
                                        Volume = position.Open.Price * position.Open.Quantity * position.Open.Leverage
                                    };

                                    await platformGrain.OnTradeExecutedAsync(tradeExecutedEvent);
                                });
                        }
                        catch (Exception ex)
                        {
                            Logger.LogWarning(ex,
                                "Failed to notify platform summary about trade execution for position {PositionId}",
                                position.Identifier);
                        }
                    }

                    position = brokerPosition;
                }
                else
                {
                    if (!position.Status.Equals(PositionStatus.New))
                    {
                        position.Status = PositionStatus.Filled;
                    }
                }
            }

            if (position.Status == PositionStatus.New)
            {
                List<Trade> orders = null;
                await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                    async exchangeService => { orders = await exchangeService.GetOpenOrders(Account, Config.Ticker); });
                if (orders.Any())
                {
                    if (orders.Count() >= 3)
                    {
                        var currentTime = Config.IsForBacktest ? LastCandle?.Date ?? DateTime.UtcNow : DateTime.UtcNow;
                        var timeSinceRequest = currentTime - positionForSignal.Open.Date;
                        var waitTimeMinutes = 10;

                        if (timeSinceRequest.TotalMinutes >= waitTimeMinutes)
                        {
                            await LogWarning(
                                $"⚠️ **Order Cleanup**\nToo many open orders: `{orders.Count()}`\nPosition: `{positionForSignal.Identifier}`\nTime elapsed: `{waitTimeMinutes}min`\nCanceling all orders...");
                            try
                            {
                                await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                                    async exchangeService =>
                                    {
                                        await exchangeService.CancelOrder(Account, Config.Ticker);
                                    });
                                await LogInformation(
                                    $"✅ **Orders Canceled**\nSuccessfully canceled all orders for: `{Config.Ticker}`");
                            }
                            catch (Exception ex)
                            {
                                await LogWarning($"Failed to cancel orders: {ex.Message}");
                            }

                            await SetPositionStatus(signal.Identifier, PositionStatus.Canceled);
                            SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                            return;
                        }
                        else
                        {
                            var remainingMinutes = waitTimeMinutes - timeSinceRequest.TotalMinutes;
                            await LogInformation(
                                $"⏳ **Waiting for Orders**\nPosition has `{orders.Count()}` open orders\nElapsed: `{timeSinceRequest.TotalMinutes:F1}min`\nWaiting `{remainingMinutes:F1}min` more before canceling");
                        }
                    }
                    else
                    {
                        await LogInformation(
                            $"⏸️ **Position Pending**\nPosition still waiting to open\n`{orders.Count()}` open orders remaining");
                    }
                }
                else
                {
                    await LogWarning(
                        $"❌ **Position Closed**\nNo position on exchange and no orders\nSignal: `{signal.Identifier}`\nPosition have been closed.");
                    await HandleClosedPosition(positionForSignal);
                }
            }
            else if (position.Status == PositionStatus.Finished || position.Status == PositionStatus.Flipped)
            {
                await HandleClosedPosition(positionForSignal);
            }
            else if (position.Status == PositionStatus.Filled || position.Status == PositionStatus.PartiallyFilled)
            {
                Candle lastCandle = null;
                await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
                {
                    lastCandle = Config.IsForBacktest
                        ? LastCandle
                        : await exchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
                });

                var currentTime = Config.IsForBacktest ? lastCandle.Date : DateTime.UtcNow;
                var currentPnl = positionForSignal.ProfitAndLoss?.Realized ?? 0;
                var pnlPercentage = positionForSignal.Open.Price * positionForSignal.Open.Quantity != 0
                    ? Math.Round((currentPnl / (positionForSignal.Open.Price * positionForSignal.Open.Quantity)) * 100,
                        2)
                    : 0;

                var isPositionInProfit = positionForSignal.OriginDirection == TradeDirection.Long
                    ? lastCandle.Close > positionForSignal.Open.Price
                    : lastCandle.Close < positionForSignal.Open.Price;

                var hasExceededTimeLimit = Config.MaxPositionTimeHours.HasValue &&
                                           HasPositionExceededTimeLimit(positionForSignal, currentTime);

                if (hasExceededTimeLimit)
                {
                    var shouldCloseOnTimeLimit = !Config.CloseEarlyWhenProfitable || isPositionInProfit;

                    if (shouldCloseOnTimeLimit)
                    {
                        var profitStatus = isPositionInProfit ? "in profit" : "at a loss";

                        await LogInformation(
                            $"⏰ **Time Limit Close**\nClosing position due to time limit: `{Config.MaxPositionTimeHours}h` exceeded\n📈 Position Status: **{profitStatus}**\n💰 Entry: `${positionForSignal.Open.Price}` → Current: `${lastCandle.Close}`\n📊 Realized PNL: `${currentPnl:F2}` (`{pnlPercentage:F2}%`)");
                        await CloseTrade(signal, positionForSignal, positionForSignal.Open, lastCandle.Close, true);
                        return;
                    }
                }

                if (positionForSignal.OriginDirection == TradeDirection.Long)
                {
                    if (positionForSignal.StopLoss.Price >= lastCandle.Low)
                    {
                        await LogInformation(
                            $"🛑 **Stop Loss Hit**\nClosing LONG position\nPrice: `${positionForSignal.StopLoss.Price}`");
                        await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
                            positionForSignal.StopLoss.Price, true);
                        positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
                    }
                    else if (positionForSignal.TakeProfit1.Price <= lastCandle.High &&
                             positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
                    {
                        await LogInformation(
                            $"🎯 **Take Profit 1 Hit**\nClosing LONG position\nPrice: `${positionForSignal.TakeProfit1.Price}`");
                        await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
                            positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
                        positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
                    }
                    else if (positionForSignal.TakeProfit2?.Price <= lastCandle.High)
                    {
                        await LogInformation(
                            $"🎯 **Take Profit 2 Hit**\nClosing LONG position\nPrice: `${positionForSignal.TakeProfit2.Price}`");
                        await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
                            positionForSignal.TakeProfit2.Price, true);
                        positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
                    }
                }
                else if (positionForSignal.OriginDirection == TradeDirection.Short)
                {
                    if (positionForSignal.StopLoss.Price <= lastCandle.High)
                    {
                        await LogInformation(
                            $"🛑 **Stop Loss Hit**\nClosing SHORT position\nPrice: `${positionForSignal.StopLoss.Price}`");
                        await CloseTrade(signal, positionForSignal, positionForSignal.StopLoss,
                            positionForSignal.StopLoss.Price, true);
                        positionForSignal.StopLoss.SetStatus(TradeStatus.Filled);
                    }
                    else if (positionForSignal.TakeProfit1.Price >= lastCandle.Low &&
                             positionForSignal.TakeProfit1.Status != TradeStatus.Filled)
                    {
                        await LogInformation(
                            $"🎯 **Take Profit 1 Hit**\nClosing SHORT position\nPrice: `${positionForSignal.TakeProfit1.Price}`");
                        await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit1,
                            positionForSignal.TakeProfit1.Price, positionForSignal.TakeProfit2 == null);
                        positionForSignal.TakeProfit1.SetStatus(TradeStatus.Filled);
                    }
                    else if (positionForSignal.TakeProfit2?.Price >= lastCandle.Low)
                    {
                        await LogInformation(
                            $"🎯 **Take Profit 2 Hit**\nClosing SHORT position\nPrice: `${positionForSignal.TakeProfit2.Price}`");
                        await CloseTrade(signal, positionForSignal, positionForSignal.TakeProfit2,
                            positionForSignal.TakeProfit2.Price, true);
                        positionForSignal.TakeProfit2.SetStatus(TradeStatus.Filled);
                    }
                }
            }
            else if (position.Status == PositionStatus.Rejected || position.Status == PositionStatus.Canceled)
            {
                await LogWarning($"Open position trade is rejected for signal {signal.Identifier}");
                if (signal.Status == SignalStatus.PositionOpen)
                {
                    Logger.LogInformation($"Try to re-open position");
                    await OpenPosition(signal);
                }
            }

            if (Config.UseSynthApi && !Config.IsForBacktest &&
                positionForSignal.Status == PositionStatus.Filled)
            {
                decimal currentPrice = 0;
                await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                    async exchangeService =>
                    {
                        currentPrice = await exchangeService.GetPrice(Account, Config.Ticker, DateTime.UtcNow);
                    });
                var riskResult = default(SynthRiskResult);
                await ServiceScopeHelpers.WithScopedService<ITradingService>(_scopeFactory, async tradingService =>
                {
                    riskResult = await tradingService.MonitorSynthPositionRiskAsync(
                        Config.Ticker,
                        positionForSignal.OriginDirection,
                        currentPrice,
                        positionForSignal.StopLoss.Price,
                        positionForSignal.Identifier,
                        Config);
                });

                if (riskResult.ShouldWarn && !string.IsNullOrEmpty(riskResult.WarningMessage))
                {
                    await LogWarning(riskResult.WarningMessage);
                }

                if (riskResult.ShouldAutoClose && !string.IsNullOrEmpty(riskResult.EmergencyMessage))
                {
                    await LogWarning(riskResult.EmergencyMessage);
                    await CloseTrade(Signals[positionForSignal.SignalIdentifier], positionForSignal,
                        positionForSignal.StopLoss,
                        currentPrice, true);
                }
            }
        }
        catch (Exception ex)
        {
            await LogWarning($"Cannot update position {positionForSignal.Identifier}: {ex.Message}, {ex.StackTrace}");
            SentrySdk.CaptureException(ex);
            return;
        }
    }

    private async Task<Position> OpenPosition(LightSignal signal)
    {
        Logger.LogInformation($"Opening position for {signal.Identifier}");

        // Check for any existing open position (not finished) for this ticker
        var openedPosition =
            Positions.Values.FirstOrDefault(p => !p.IsFinished() && p.SignalIdentifier != signal.Identifier);

        decimal lastPrice = await ServiceScopeHelpers.WithScopedService<IExchangeService, decimal>(_scopeFactory,
            async exchangeService =>
            {
                return Config.IsForBacktest
                    ? LastCandle?.Close ?? 0
                    : await exchangeService.GetPrice(Account, Config.Ticker, DateTime.UtcNow);
            });

        if (openedPosition != null)
        {
            var previousSignal = Signals[openedPosition.SignalIdentifier];

            if (openedPosition.OriginDirection == signal.Direction)
            {
                await LogInformation(
                    $"📍 **Same Direction Signal**\nSignal `{signal.Identifier}` tried to open position\nBut `{previousSignal.Identifier}` already open for same direction");
                SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                return null;
            }
            else
            {
                if (Config.FlipPosition)
                {
                    var isPositionInProfit = (openedPosition.ProfitAndLoss?.Realized ?? 0) > 0;
                    var shouldFlip = !Config.FlipOnlyWhenInProfit || isPositionInProfit;

                    if (shouldFlip)
                    {
                        var flipReason = Config.FlipOnlyWhenInProfit
                            ? "current position is in profit"
                            : "FlipOnlyWhenInProfit is disabled";

                        await LogInformation(
                            $"🔄 **Position Flip Initiated**\nFlipping position due to opposite signal\nReason: {flipReason}");
                        await CloseTrade(previousSignal, openedPosition, openedPosition.Open, lastPrice, true);
                        await SetPositionStatus(previousSignal.Identifier, PositionStatus.Flipped);
                        var newPosition = await OpenPosition(signal);
                        await LogInformation(
                            $"✅ **Position Flipped**\nPosition: `{previousSignal.Identifier}` → `{signal.Identifier}`\nPrice: `${lastPrice}`");
                        return newPosition;
                    }
                    else
                    {
                        var currentPnl = openedPosition.ProfitAndLoss?.Realized ?? 0;
                        await LogInformation(
                            $"💸 **Flip Blocked - Not Profitable**\nPosition `{previousSignal.Identifier}` PnL: `${currentPnl:F2}`\nSignal `{signal.Identifier}` will wait for profitability");

                        SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                        return null;
                    }
                }
                else
                {
                    await LogInformation(
                        $"🚫 **Flip Disabled**\nPosition already open for: `{previousSignal.Identifier}`\nFlipping disabled, new signal expired");
                    SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                    return null;
                }
            }
        }
        else
        {
            bool canOpen = await CanOpenPosition(signal);
            if (!canOpen)
            {
                SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                return null;
            }

            await LogInformation(
                $"🚀 **Opening Position**\nTime: `{signal.Date:HH:mm:ss}`\nSignal: `{signal.Identifier}`");

            try
            {
                var command = new OpenPositionRequest(
                    Config.AccountName,
                    Config.MoneyManagement,
                    signal.Direction,
                    Config.Ticker,
                    PositionInitiator.Bot,
                    signal.Date,
                    Account.User,
                    Config.BotTradingBalance,
                    Config.IsForBacktest,
                    lastPrice,
                    signalIdentifier: signal.Identifier,
                    initiatorIdentifier: Identifier);

                var position = await ServiceScopeHelpers
                    .WithScopedServices<IExchangeService, IAccountService, ITradingService, Position>(
                        _scopeFactory,
                        async (exchangeService, accountService, tradingService) =>
                        {
                            return await new OpenPositionCommandHandler(exchangeService, accountService, tradingService)
                                .Handle(command);
                        });

                if (position != null)
                {
                    if (position.Open.Status != TradeStatus.Cancelled)
                    {
                        SetSignalStatus(signal.Identifier, SignalStatus.PositionOpen);

                        if (!Config.IsForBacktest)
                        {
                            await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
                                async messengerService => { await messengerService.SendPosition(position); });
                        }

                        Logger.LogInformation($"Position requested");
                        return position; // Return the created position without adding to list
                    }
                    else
                    {
                        await SetPositionStatus(signal.Identifier, PositionStatus.Rejected);
                        SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                        return null;
                    }
                }

                return null;
            }
            catch (Exception ex)
            {
                SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                await LogWarning($"Cannot open trade : {ex.Message}, stackTrace : {ex.StackTrace}");
                return null;
            }
        }
    }

    private async Task<bool> CanOpenPosition(LightSignal signal)
    {
        // Early return if we're in backtest mode and haven't executed yet
        // TODO : check if its a startup cycle
        if (!Config.IsForBacktest && ExecutionCount == 0)
        {
            await LogInformation("⏳ **Bot Not Ready**\nCannot open position\nBot hasn't executed first cycle yet");
            return false;
        }

        // Check if we're in backtest mode
        if (Config.IsForBacktest)
        {
            return !IsInCooldownPeriod() && await CheckLossStreak(signal);
        }

        // Check broker positions for live trading
        var canOpenPosition = await CheckBrokerPositions();
        if (!canOpenPosition)
        {
            return false;
        }

        // Synth-based pre-trade risk assessment
        if (Config.UseSynthApi)
        {
            decimal currentPrice = 0;
            await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
            {
                currentPrice = Config.IsForBacktest
                    ? LastCandle?.Close ?? 0
                    : await exchangeService.GetPrice(Account, Config.Ticker, DateTime.UtcNow);
            });


            bool synthRisk = false;
            await ServiceScopeHelpers.WithScopedService<ITradingService>(_scopeFactory, async tradingService =>
            {
                synthRisk = await tradingService.AssessSynthPositionRiskAsync(Config.Ticker, signal.Direction,
                    currentPrice,
                    Config, Config.IsForBacktest);
            });
            if (!synthRisk)
            {
                return false;
            }
        }

        // Check cooldown period and loss streak
        return !IsInCooldownPeriod() && await CheckLossStreak(signal);
    }

    private async Task<bool> CheckLossStreak(LightSignal signal)
    {
        // If MaxLossStreak is 0, there's no limit
        if (Config.MaxLossStreak <= 0)
        {
            return true;
        }

        // Get the last N finished positions regardless of direction
        var recentPositions = Positions
            .Values
            .Where(p => p.IsFinished())
            .OrderByDescending(p => p.Open.Date)
            .Take(Config.MaxLossStreak)
            .ToList();

        // If we don't have enough positions to form a streak, we can open
        if (recentPositions.Count < Config.MaxLossStreak)
        {
            return true;
        }

        // Check if all recent positions were losses
        var allLosses = recentPositions.All(p => p.ProfitAndLoss?.Realized < 0);
        if (!allLosses)
        {
            return true;
        }

        // If we have a loss streak, check if the last position was in the same direction as the signal
        var lastPosition = recentPositions.First();
        if (lastPosition.OriginDirection == signal.Direction)
        {
            await LogWarning(
                $"🔥 **Loss Streak Limit**\nCannot open position\nMax loss streak: `{Config.MaxLossStreak}` reached\n📉 Last `{recentPositions.Count}` trades were losses\n🎯 Last position: `{lastPosition.OriginDirection}`\nWaiting for opposite direction signal");
            return false;
        }

        return true;
    }

    private async Task<bool> CheckBrokerPositions()
    {
        try
        {
            List<Position> positions = null;
            await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                async exchangeService => { positions = (await exchangeService.GetBrokerPositions(Account)).ToList(); });
            if (!positions.Any(p => p.Ticker == Config.Ticker))
            {
                return true;
            }

            // Handle existing position on broker
            var previousPosition = Positions.Values.LastOrDefault();
            List<Trade> orders = null;
            await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                async exchangeService =>
                {
                    orders = (await exchangeService.GetOpenOrders(Account, Config.Ticker)).ToList();
                });

            var reason = $"Cannot open position. There is already a position open for {Config.Ticker} on the broker.";

            if (previousPosition != null && orders.Count >= 2)
            {
                await SetPositionStatus(previousPosition.SignalIdentifier, PositionStatus.Filled);
            }
            else
            {
                reason +=
                    " Position open on broker but not enough orders or no previous position internally saved by the bot";
            }

            await LogWarning(reason);
            return false;
        }
        catch (Exception ex)
        {
            await LogWarning($"❌ **Broker Position Check Failed**\nError checking broker positions\n{ex.Message}");
            return false;
        }
    }


    /// <summary>
    /// Gets the actual closing date of a position by checking which trade (Stop Loss or Take Profit) was executed.
    /// </summary>
    /// <param name="position">The finished position</param>
    /// <returns>The date when the position was closed, or null if cannot be determined</returns>
    private DateTime? GetPositionClosingDate(Position position)
    {
        if (!position.IsFinished())
        {
            return null;
        }

        // Check which trade actually closed the position
        if (position.StopLoss?.Status == TradeStatus.Filled && position.StopLoss.Date != default)
        {
            return position.StopLoss.Date;
        }

        if (position.TakeProfit1?.Status == TradeStatus.Filled && position.TakeProfit1.Date != default)
        {
            return position.TakeProfit1.Date;
        }

        if (position.TakeProfit2?.Status == TradeStatus.Filled && position.TakeProfit2.Date != default)
        {
            return position.TakeProfit2.Date;
        }

        // Fallback: if we can't determine the exact closing trade, use the latest date available
        var availableDates = new List<DateTime>();

        if (position.StopLoss?.Date != default)
            availableDates.Add(position.StopLoss.Date);

        if (position.TakeProfit1?.Date != default)
            availableDates.Add(position.TakeProfit1.Date);

        if (position.TakeProfit2?.Date != default)
            availableDates.Add(position.TakeProfit2.Date);

        return availableDates.Any() ? availableDates.Max() : position.Open.Date;
    }

    public async Task CloseTrade(LightSignal signal, Position position, Trade tradeToClose, decimal lastPrice,
        bool tradeClosingPosition = false)
    {
        if (position.TakeProfit2 != null && position.TakeProfit1.Status == TradeStatus.Filled &&
            tradeToClose.TradeType == TradeType.StopMarket)
        {
            // If trade is the 2nd Take profit
            tradeToClose.Quantity = position.TakeProfit2.Quantity;
        }

        await LogInformation(
            $"🔧 **Closing Trade**\nTicker: `{Config.Ticker}`\nPrice: `${lastPrice}`\n📋 Type: `{tradeToClose.TradeType}`\n📊 Quantity: `{tradeToClose.Quantity}`\n🎯 Closing Position: `{(tradeClosingPosition ? "Yes" : "No")}`");

        decimal quantity = 0;

        if (!Config.IsForBacktest)
        {
            await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                async exchangeService =>
                {
                    quantity = await exchangeService.GetQuantityInPosition(Account, Config.Ticker);
                });
        }

        // Get status of position before closing it. The position might be already close by the exchange
        if (!Config.IsForBacktest && quantity == 0)
        {
            Logger.LogInformation($"Trade already close on exchange");
            await HandleClosedPosition(position);
        }
        else
        {
            var command = new ClosePositionCommand(position, lastPrice, isForBacktest: Config.IsForBacktest);
            try
            {
                Position closedPosition = null;
                await ServiceScopeHelpers.WithScopedServices<IExchangeService, IAccountService, ITradingService>(
                    _scopeFactory, async (exchangeService, accountService, tradingService) =>
                    {
                        closedPosition =
                            await new ClosePositionCommandHandler(exchangeService, accountService, tradingService,
                                    _scopeFactory)
                                .Handle(command);
                    });

                if (closedPosition.Status == PositionStatus.Finished || closedPosition.Status == PositionStatus.Flipped)
                {
                    if (tradeClosingPosition)
                    {
                        await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
                    }

                    await HandleClosedPosition(closedPosition);
                }
                else
                {
                    throw new Exception($"Wrong position status : {closedPosition.Status}");
                }
            }
            catch (Exception ex)
            {
                await LogWarning($"Position {signal.Identifier} not closed : {ex.Message}");

                if (position.Status == PositionStatus.Canceled || position.Status == PositionStatus.Rejected)
                {
                    // Trade close on exchange => Should close trade manually
                    await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
                }
            }
        }
    }

    private async Task HandleClosedPosition(Position position)
    {
        if (Positions.ContainsKey(position.Identifier))
        {
            Candle currentCandle = null;
            await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
            {
                currentCandle = Config.IsForBacktest
                    ? LastCandle
                    : await exchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);
            });

            if (currentCandle != null)
            {
                List<Candle> recentCandles = null;
                await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
                {
                    recentCandles = Config.IsForBacktest
                        ? (LastCandle != null ? new List<Candle>() { LastCandle } : new List<Candle>())
                        : (await exchangeService.GetCandlesInflux(TradingExchanges.Evm, Config.Ticker,
                            DateTime.UtcNow.AddHours(-4), Config.Timeframe)).ToList();
                });

                // Check if we have any candles before proceeding
                if (recentCandles == null || !recentCandles.Any())
                {
                    await LogWarning(
                        $"No recent candles available for position {position.Identifier}. Using current candle data instead.");

                    // Fallback to current candle if available
                    if (currentCandle != null)
                    {
                        recentCandles = new List<Candle> { currentCandle };
                    }
                    else
                    {
                        await LogWarning(
                            $"No candle data available for position {position.Identifier}. Cannot determine stop loss/take profit hit.");
                        Logger.LogError(
                            "No candle data available for position {PositionId}. Cannot determine stop loss/take profit hit.",
                            position.Identifier);
                        return;
                    }
                }

                var minPriceRecent = recentCandles.Min(c => c.Low);
                var maxPriceRecent = recentCandles.Max(c => c.High);

                bool wasStopLossHit = false;
                bool wasTakeProfitHit = false;

                if (position.OriginDirection == TradeDirection.Long)
                {
                    wasStopLossHit = minPriceRecent <= position.StopLoss.Price;
                    wasTakeProfitHit = maxPriceRecent >= position.TakeProfit1.Price;
                }
                else
                {
                    wasStopLossHit = maxPriceRecent >= position.StopLoss.Price;
                    wasTakeProfitHit = minPriceRecent <= position.TakeProfit1.Price;
                }

                decimal closingPrice;

                if (wasStopLossHit)
                {
                    closingPrice = position.StopLoss.Price;
                    position.StopLoss.SetDate(currentCandle.Date);

                    Logger.LogInformation(
                        $"🛑 **Stop Loss Execution Confirmed**\n" +
                        $"Position: `{position.Identifier}`\n" +
                        $"SL Price: `${position.StopLoss.Price:F2}` was hit\n" +
                        $"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
                }
                else if (wasTakeProfitHit)
                {
                    closingPrice = position.TakeProfit1.Price;
                    position.TakeProfit1.SetDate(currentCandle.Date);

                    Logger.LogInformation(
                        $"🎯 **Take Profit Execution Confirmed**\n" +
                        $"Position: `{position.Identifier}`\n" +
                        $"TP Price: `${position.TakeProfit1.Price:F2}` was hit\n" +
                        $"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`");
                }
                else
                {
                    closingPrice = Config.IsForBacktest
                        ? currentCandle.Close
                        : 0;
                    await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                        async exchangeService =>
                        {
                            closingPrice = await exchangeService.GetPrice(Account, Config.Ticker, DateTime.UtcNow);
                        });

                    bool isManualCloseProfitable = position.OriginDirection == TradeDirection.Long
                        ? closingPrice > position.Open.Price
                        : closingPrice < position.Open.Price;

                    if (isManualCloseProfitable)
                    {
                        position.TakeProfit1.SetDate(currentCandle.Date);
                    }
                    else
                    {
                        position.StopLoss.SetDate(currentCandle.Date);
                    }

                    Logger.LogInformation(
                        $"✋ **Manual/Exchange Close Detected**\n" +
                        $"Position: `{position.Identifier}`\n" +
                        $"SL: `${position.StopLoss.Price:F2}` | TP: `${position.TakeProfit1.Price:F2}`\n" +
                        $"Recent Low: `${minPriceRecent:F2}` | Recent High: `${maxPriceRecent:F2}`\n" +
                        $"Closing at market price: `${closingPrice:F2}`");
                }

                var entryPrice = position.Open.Price;
                var positionSize = position.Open.Quantity * position.Open.Leverage;

                decimal pnl;
                if (position.OriginDirection == TradeDirection.Long)
                {
                    pnl = (closingPrice - entryPrice) * positionSize;
                }
                else
                {
                    pnl = (entryPrice - closingPrice) * positionSize;
                }

                if (position.ProfitAndLoss == null)
                {
                    position.ProfitAndLoss = new ProfitAndLoss { Realized = pnl };
                }
                else if (position.ProfitAndLoss.Realized == 0)
                {
                    position.ProfitAndLoss.Realized = pnl;
                }
            }

            await SetPositionStatus(position.SignalIdentifier, PositionStatus.Finished);

            // Update the last position closing time for cooldown period tracking
            LastPositionClosingTime = Config.IsForBacktest ? currentCandle.Date : DateTime.UtcNow;

            Logger.LogInformation(
                $"✅ **Position Closed Successfully**\nPosition: `{position.SignalIdentifier}`\nPnL: `${position.ProfitAndLoss?.Realized:F2}`");

            if (position.ProfitAndLoss != null)
            {
                Config.BotTradingBalance += position.ProfitAndLoss.Realized;

                Logger.LogInformation(
                    $"💰 **Balance Updated**\nNew bot trading balance: `${Config.BotTradingBalance:F2}`");
            }
        }
        else
        {
            await LogWarning("Weird things happen - Trying to update position status, but no position found");
        }

        if (!Config.IsForBacktest)
        {
            await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
                async messengerService => { await messengerService.SendClosingPosition(position); });
        }

        await CancelAllOrders();
    }

    private async Task CancelAllOrders()
    {
        if (!Config.IsForBacktest && !Config.IsForWatchingOnly)
        {
            try
            {
                List<Trade> openOrders = null;
                await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                    async exchangeService =>
                    {
                        openOrders = (await exchangeService.GetOpenOrders(Account, Config.Ticker)).ToList();
                    });
                if (openOrders.Any())
                {
                    List<Position> openPositions = null;
                    await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                        async exchangeService =>
                        {
                            openPositions = (await exchangeService.GetBrokerPositions(Account))
                                .Where(p => p.Ticker == Config.Ticker).ToList();
                        });
                    var cancelClose = openPositions.Any();

                    if (cancelClose)
                    {
                        Logger.LogInformation($"Position still open, cancel close orders");
                    }
                    else
                    {
                        Logger.LogInformation($"Canceling all orders for {Config.Ticker}");
                        await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory,
                            async exchangeService =>
                            {
                                await exchangeService.CancelOrder(Account, Config.Ticker);
                                var closePendingOrderStatus = await exchangeService.CancelOrder(Account, Config.Ticker);
                                Logger.LogInformation(
                                    $"Closing all {Config.Ticker} orders status : {closePendingOrderStatus}");
                            });
                    }
                }
                else
                {
                    Logger.LogInformation($"No need to cancel orders for {Config.Ticker}");
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error during cancelOrders");
                SentrySdk.CaptureException(ex);
            }
        }
    }

    private async Task SetPositionStatus(string signalIdentifier, PositionStatus positionStatus)
    {
        try
        {
            var position = Positions.Values.First(p => p.SignalIdentifier == signalIdentifier);
            if (!position.Status.Equals(positionStatus))
            {
                Positions.Values.First(p => p.SignalIdentifier == signalIdentifier).Status = positionStatus;
                await LogInformation(
                    $"📊 **Position Status Change**\nPosition: `{signalIdentifier}`\nStatus: `{position.Status}` → `{positionStatus}`");
            }

            SetSignalStatus(signalIdentifier,
                positionStatus == PositionStatus.Filled ? SignalStatus.PositionOpen : SignalStatus.Expired);
        }
        catch (Exception ex)
        {
            await LogWarning($"Failed to update position status for signal {signalIdentifier}: {ex.Message}");
        }
    }

    private void UpdatePositionPnl(Guid identifier, decimal realized)
    {
        Positions[identifier].ProfitAndLoss = new ProfitAndLoss()
        {
            Realized = realized
        };
    }

    private void SetSignalStatus(string signalIdentifier, SignalStatus signalStatus)
    {
        if (Signals.ContainsKey(signalIdentifier) && Signals[signalIdentifier].Status != signalStatus)
        {
            Signals[signalIdentifier].Status = signalStatus;
            Logger.LogInformation($"Signal {signalIdentifier} is now {signalStatus}");
        }
    }

    public int GetWinRate()
    {
        var succeededPositions = Positions.Values.Where(p => p.IsFinished()).Count(p => p.ProfitAndLoss?.Realized > 0);
        var total = Positions.Values.Where(p => p.IsFinished()).Count();

        if (total == 0)
            return 0;

        return (succeededPositions * 100) / total;
    }

    public decimal GetProfitAndLoss()
    {
        var pnl = Positions.Values.Where(p => p.ProfitAndLoss != null && p.IsFinished())
            .Sum(p => p.ProfitAndLoss.Realized);
        return pnl - GetTotalFees();
    }

    /// <summary>
    /// Calculates the total fees paid by the trading bot for each position.
    /// Includes UI fees (0.1% of position size) and network fees ($0.50 for opening).
    /// Closing fees are handled by oracle, so no network fee for closing.
    /// </summary>
    /// <returns>Returns the total fees paid as a decimal value.</returns>
    public decimal GetTotalFees()
    {
        decimal totalFees = 0;

        foreach (var position in Positions.Values.Where(p => p.Open.Price > 0 && p.Open.Quantity > 0))
        {
            totalFees += CalculatePositionFees(position);
        }

        return totalFees;
    }

    /// <summary>
    /// Calculates the total fees for a specific position based on GMX V2 fee structure
    /// </summary>
    /// <param name="position">The position to calculate fees for</param>
    /// <returns>The total fees for the position</returns>
    private decimal CalculatePositionFees(Position position)
    {
        decimal fees = 0;

        // Calculate position size in USD (leverage is already included in quantity calculation)
        var positionSizeUsd = (position.Open.Price * position.Open.Quantity) * position.Open.Leverage;

        // UI Fee: 0.1% of position size paid on opening
        var uiFeeRate = 0.001m; // 0.1%
        var uiFeeOpen = positionSizeUsd * uiFeeRate; // Fee paid on opening
        fees += uiFeeOpen;

        // UI Fee: 0.1% of position size paid on closing - only if position was actually closed
        // Check which closing trade was executed (StopLoss, TakeProfit1, or TakeProfit2)
        // Calculate closing fee based on the actual executed trade's price and quantity
        if (position.StopLoss?.Status == TradeStatus.Filled)
        {
            var stopLossPositionSizeUsd =
                (position.StopLoss.Price * position.StopLoss.Quantity) * position.StopLoss.Leverage;
            var uiFeeClose = stopLossPositionSizeUsd * uiFeeRate; // Fee paid on closing via StopLoss
            fees += uiFeeClose;
        }
        else if (position.TakeProfit1?.Status == TradeStatus.Filled)
        {
            var takeProfit1PositionSizeUsd = (position.TakeProfit1.Price * position.TakeProfit1.Quantity) *
                                             position.TakeProfit1.Leverage;
            var uiFeeClose = takeProfit1PositionSizeUsd * uiFeeRate; // Fee paid on closing via TakeProfit1
            fees += uiFeeClose;
        }
        else if (position.TakeProfit2?.Status == TradeStatus.Filled)
        {
            var takeProfit2PositionSizeUsd = (position.TakeProfit2.Price * position.TakeProfit2.Quantity) *
                                             position.TakeProfit2.Leverage;
            var uiFeeClose = takeProfit2PositionSizeUsd * uiFeeRate; // Fee paid on closing via TakeProfit2
            fees += uiFeeClose;
        }

        // Network Fee: $0.50 for opening position only
        // Closing is handled by oracle, so no network fee for closing
        var networkFeeForOpening = 0.50m;
        fees += networkFeeForOpening;

        return fees;
    }


    public async Task ToggleIsForWatchOnly()
    {
        Config.IsForWatchingOnly = !Config.IsForWatchingOnly;
        await LogInformation(
            $"🔄 **Watch Mode Toggle**\nBot: `{Config.Name}`\nWatch Only: `{(Config.IsForWatchingOnly ? "ON" : "OFF")}`");
    }

    public async Task LogInformation(string message)
    {
        Logger.LogInformation(message);

        if (Config.IsForBacktest)
            return;

        try
        {
            await SendTradeMessage(message);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }

    public async Task LogWarning(string message)
    {
        message = $"[{Config.Name}] {message}";
        SentrySdk.CaptureException(new Exception(message));

        try
        {
            await SendTradeMessage(message, true);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }

    private async Task SendTradeMessage(string message, bool isBadBehavior = false)
    {
        if (!Config.IsForBacktest)
        {
            var user = Account.User;
            var messageWithBotName = $"🤖 **{user.AgentName} - {Config.Name}**\n{message}";
            await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
                async messengerService =>
                {
                    await messengerService.SendTradeMessage(messageWithBotName, isBadBehavior, user);
                });
        }
    }

    /// <summary>
    /// Manually opens a position using the bot's settings and a generated signal.
    /// Relies on the bot's MoneyManagement for Stop Loss and Take Profit placement.
    /// </summary>
    /// <param name="direction">The direction of the trade (Long/Short).</param>
    /// <returns>The created Position object.</returns>
    /// <exception cref="Exception">Throws if no candles are available or position opening fails.</exception>
    public async Task<Position> OpenPositionManually(TradeDirection direction)
    {
        if (LastCandle == null)
        {
            throw new Exception("No candles available to open position");
        }

        // Create a fake signal for manual position opening
        var signal = new LightSignal(Config.Ticker, direction, Confidence.Low, LastCandle, LastCandle.Date,
            TradingExchanges.GmxV2,
            IndicatorType.Stc, SignalType.Signal, "Manual Signal");
        signal.Status = SignalStatus.WaitingForPosition; // Ensure status is correct
        signal.Identifier =
            signal.Identifier + "-manual" + Guid.NewGuid(); // Ensure unique identifier for manual signals

        // Add the signal to our collection
        await AddSignal(signal);

        // Open the position using the generated signal (SL/TP handled by MoneyManagement)
        var position = await OpenPosition(signal);

        if (position == null)
        {
            // Clean up the signal if position creation failed
            SetSignalStatus(signal.Identifier, SignalStatus.Expired);
            throw new Exception("Failed to open position");
        }

        // Add the position to the list after successful creation
        Positions[position.Identifier] = position;

        Logger.LogInformation(
            $"👤 **Manual Position Opened**\nPosition: `{position.Identifier}`\nSignal: `{signal.Identifier}`\nAdded to positions list");
        return position;
    }

    public async Task AddSignal(LightSignal signal)
    {
        try
        {
            // Set signal status based on configuration
            if (Config.IsForWatchingOnly || (ExecutionCount < 1 && !Config.IsForBacktest))
            {
                signal.Status = SignalStatus.Expired;
            }

            var indicatorNames = Config.Scenario.Indicators.Select(i => i.Type.ToString()).ToList();
            var signalText = $"🎯 **New Trading Signal**\n\n" +
                             $"📊 **Signal Details:**\n" +
                             $"📈 Action: `{signal.Direction}` {Config.Ticker}\n" +
                             $"⏰ Timeframe: `{Config.Timeframe}`\n" +
                             $"🎯 Confidence: `{signal.Confidence}`\n" +
                             $"🔍 Indicators: `{string.Join(", ", indicatorNames)}`\n" +
                             $"🆔 Signal ID: `{signal.Identifier}`";

            // Apply Synth-based signal filtering if enabled
            if ((Config.UseSynthApi || !Config.IsForBacktest) && ExecutionCount > 0)
            {
                await ServiceScopeHelpers.WithScopedServices<ITradingService, IExchangeService>(_scopeFactory,
                    async (tradingService, exchangeService) =>
                    {
                        var currentPrice = await exchangeService.GetPrice(Account, Config.Ticker, DateTime.UtcNow);

                        var signalValidationResult = await tradingService.ValidateSynthSignalAsync(
                            signal,
                            currentPrice,
                            Config,
                            Config.IsForBacktest);

                        if (signalValidationResult.Confidence == Confidence.None ||
                            signalValidationResult.Confidence == Confidence.Low ||
                            signalValidationResult.IsBlocked)
                        {
                            signal.Status = SignalStatus.Expired;
                            Logger.LogInformation($"Signal {signal.Identifier} blocked by Synth risk assessment");
                        }
                        else
                        {
                            signal.Confidence = signalValidationResult.Confidence;
                            Logger.LogInformation(
                                $"Signal {signal.Identifier} passed Synth risk assessment with confidence {signalValidationResult.Confidence}");
                        }
                    });
            }

            Signals.Add(signal.Identifier, signal);

            Logger.LogInformation(signalText);

            if (Config.IsForWatchingOnly && !Config.IsForBacktest && ExecutionCount > 0)
            {
                await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory, async messengerService =>
                {
                    await messengerService.SendSignal(signalText, Account.Exchange, Config.Ticker, signal.Direction,
                        Config.Timeframe);
                });
            }

            Logger.LogInformation(
                $"Processed signal for {Config.Ticker}: {signal.Direction} with status {signal.Status}");
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Failed to add signal for {Ticker}", Config.Ticker);
            throw;
        }
    }

    /// <summary>
    /// Checks if a position has exceeded the maximum time limit for being open.
    /// </summary>
    /// <param name="position">The position to check</param>
    /// <param name="currentTime">The current time to compare against</param>
    /// <returns>True if the position has exceeded the time limit, false otherwise</returns>
    private bool HasPositionExceededTimeLimit(Position position, DateTime currentTime)
    {
        if (!Config.MaxPositionTimeHours.HasValue || Config.MaxPositionTimeHours.Value <= 0)
        {
            return false; // Time-based closure is disabled
        }

        var timeOpen = currentTime - position.Open.Date;
        var maxTimeAllowed = TimeSpan.FromHours((double)Config.MaxPositionTimeHours.Value);

        return timeOpen >= maxTimeAllowed;
    }

    /// <summary>
    /// Updates the trading bot configuration with new settings.
    /// This method validates the new configuration and applies it to the running bot.
    /// </summary>
    /// <param name="newConfig">The new configuration to apply</param>
    /// <returns>True if the configuration was successfully updated, false otherwise</returns>
    /// <exception cref="ArgumentException">Thrown when the new configuration is invalid</exception>
    public async Task<bool> UpdateConfiguration(TradingBotConfig newConfig)
    {
        try
        {
            // Validate the new configuration
            if (newConfig == null)
            {
                throw new ArgumentException("Configuration cannot be null");
            }

            if (newConfig.BotTradingBalance <= Constants.GMX.Config.MinimumPositionAmount)
            {
                throw new ArgumentException(
                    $"Bot trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}");
            }

            if (string.IsNullOrEmpty(newConfig.AccountName))
            {
                throw new ArgumentException("Account name cannot be null or empty");
            }

            if (newConfig.Scenario == null)
            {
                throw new ArgumentException("Scenario object must be provided in configuration");
            }

            // Track changes for logging
            var changes = new List<string>();

            // Check for changes and build change list
            if (Config.BotTradingBalance != newConfig.BotTradingBalance)
            {
                changes.Add($"💰 Balance: ${Config.BotTradingBalance:F2} → ${newConfig.BotTradingBalance:F2}");
            }

            if (Config.MaxPositionTimeHours != newConfig.MaxPositionTimeHours)
            {
                var oldTime = Config.MaxPositionTimeHours?.ToString() + "h" ?? "Disabled";
                var newTime = newConfig.MaxPositionTimeHours?.ToString() + "h" ?? "Disabled";
                changes.Add($"⏱️ Max Time: {oldTime} → {newTime}");
            }

            if (Config.FlipOnlyWhenInProfit != newConfig.FlipOnlyWhenInProfit)
            {
                var oldFlip = Config.FlipOnlyWhenInProfit ? "✅" : "❌";
                var newFlip = newConfig.FlipOnlyWhenInProfit ? "✅" : "❌";
                changes.Add($"📈 Flip Only in Profit: {oldFlip} → {newFlip}");
            }

            if (Config.CooldownPeriod != newConfig.CooldownPeriod)
            {
                changes.Add($"⏳ Cooldown: {Config.CooldownPeriod} → {newConfig.CooldownPeriod} candles");
            }

            if (Config.MaxLossStreak != newConfig.MaxLossStreak)
            {
                changes.Add($"📉 Max Loss Streak: {Config.MaxLossStreak} → {newConfig.MaxLossStreak}");
            }

            if (Config.FlipPosition != newConfig.FlipPosition)
            {
                var oldFlipPos = Config.FlipPosition ? "✅" : "❌";
                var newFlipPos = newConfig.FlipPosition ? "✅" : "❌";
                changes.Add($"🔄 Flip Position: {oldFlipPos} → {newFlipPos}");
            }

            if (Config.CloseEarlyWhenProfitable != newConfig.CloseEarlyWhenProfitable)
            {
                var oldCloseEarly = Config.CloseEarlyWhenProfitable ? "✅" : "❌";
                var newCloseEarly = newConfig.CloseEarlyWhenProfitable ? "✅" : "❌";
                changes.Add($"⏰ Close Early When Profitable: {oldCloseEarly} → {newCloseEarly}");
            }

            if (Config.UseSynthApi != newConfig.UseSynthApi)
            {
                var oldSynth = Config.UseSynthApi ? "✅" : "❌";
                var newSynth = newConfig.UseSynthApi ? "✅" : "❌";
                changes.Add($"🔗 Use Synth API: {oldSynth} → {newSynth}");
            }

            if (Config.UseForPositionSizing != newConfig.UseForPositionSizing)
            {
                var oldPositionSizing = Config.UseForPositionSizing ? "✅" : "❌";
                var newPositionSizing = newConfig.UseForPositionSizing ? "✅" : "❌";
                changes.Add($"📏 Use Synth for Position Sizing: {oldPositionSizing} → {newPositionSizing}");
            }

            if (Config.UseForSignalFiltering != newConfig.UseForSignalFiltering)
            {
                var oldSignalFiltering = Config.UseForSignalFiltering ? "✅" : "❌";
                var newSignalFiltering = newConfig.UseForSignalFiltering ? "✅" : "❌";
                changes.Add($"🔍 Use Synth for Signal Filtering: {oldSignalFiltering} → {newSignalFiltering}");
            }

            if (Config.UseForDynamicStopLoss != newConfig.UseForDynamicStopLoss)
            {
                var oldDynamicStopLoss = Config.UseForDynamicStopLoss ? "✅" : "❌";
                var newDynamicStopLoss = newConfig.UseForDynamicStopLoss ? "✅" : "❌";
                changes.Add($"🎯 Use Synth for Dynamic Stop Loss: {oldDynamicStopLoss} → {newDynamicStopLoss}");
            }

            if (Config.IsForWatchingOnly != newConfig.IsForWatchingOnly)
            {
                var oldWatch = Config.IsForWatchingOnly ? "✅" : "❌";
                var newWatch = newConfig.IsForWatchingOnly ? "✅" : "❌";
                changes.Add($"👀 Watch Only: {oldWatch} → {newWatch}");
            }

            if (Config.MoneyManagement?.GetType().Name != newConfig.MoneyManagement?.GetType().Name)
            {
                var oldMM = Config.MoneyManagement?.GetType().Name ?? "None";
                var newMM = newConfig.MoneyManagement?.GetType().Name ?? "None";
                changes.Add($"💰 Money Management: {oldMM} → {newMM}");
            }

            if (Config.RiskManagement != newConfig.RiskManagement)
            {
                // Compare risk management by serializing (complex object comparison)
                var oldRiskSerialized = JsonConvert.SerializeObject(Config.RiskManagement, Formatting.None);
                var newRiskSerialized = JsonConvert.SerializeObject(newConfig.RiskManagement, Formatting.None);

                if (oldRiskSerialized != newRiskSerialized)
                {
                    changes.Add($"⚠️ Risk Management: Configuration Updated");
                }
            }

            if (Config.ScenarioName != newConfig.ScenarioName)
            {
                changes.Add($"📋 Scenario Name: {Config.ScenarioName ?? "None"} → {newConfig.ScenarioName ?? "None"}");
            }

            if (Config.Name != newConfig.Name)
            {
                changes.Add($"🏷️ Name: {Config.Name} → {newConfig.Name}");
            }

            if (Config.AccountName != newConfig.AccountName)
            {
                changes.Add($"👤 Account: {Config.AccountName} → {newConfig.AccountName}");
            }

            if (Config.Ticker != newConfig.Ticker)
            {
                changes.Add($"📊 Ticker: {Config.Ticker} → {newConfig.Ticker}");
            }

            if (Config.Timeframe != newConfig.Timeframe)
            {
                changes.Add($"📈 Timeframe: {Config.Timeframe} → {newConfig.Timeframe}");
            }

            // Check if the actual Scenario object changed (not just the name)
            var scenarioChanged = false;
            if (Config.Scenario != newConfig.Scenario)
            {
                var oldScenarioSerialized = JsonConvert.SerializeObject(Config.Scenario, Formatting.None);
                var newScenarioSerialized = JsonConvert.SerializeObject(newConfig.Scenario, Formatting.None);

                if (oldScenarioSerialized != newScenarioSerialized)
                {
                    scenarioChanged = true;
                    changes.Add(
                        $"🎯 Scenario: {Config.Scenario?.Name ?? "None"} → {newConfig.Scenario?.Name ?? "None"}");
                }
            }

            // Protect critical properties that shouldn't change for running bots
            var protectedIsForBacktest = Config.IsForBacktest;

            // Update the configuration
            Config = newConfig;

            // Restore protected properties
            Config.IsForBacktest = protectedIsForBacktest;

            // Update bot name and identifier if allowed
            if (!string.IsNullOrEmpty(newConfig.Name))
            {
                Config.Name = newConfig.Name;
            }

            // If account changed, reload it
            if (Config.AccountName != Account?.Name)
            {
                await LoadAccount();
            }

            // If scenario changed, reload it and track indicator changes
            if (scenarioChanged)
            {
                if (newConfig.Scenario != null)
                {
                    // Compare indicators after scenario change
                    var newIndicators = newConfig.Scenario.Indicators?.ToList() ?? new List<LightIndicator>();
                    var indicatorChanges = ScenarioHelpers.CompareIndicators(Config.Scenario.Indicators, newIndicators);

                    if (indicatorChanges.Any())
                    {
                        changes.AddRange(indicatorChanges);
                    }
                }
                else
                {
                    throw new ArgumentException("New scenario object must be provided when updating configuration.");
                }
            }

            // Only log if there are actual changes
            if (changes.Any())
            {
                var changeMessage = "⚙️ **Configuration Updated**\n" + string.Join("\n", changes);
                await LogInformation(changeMessage);
            }
            else
            {
                await LogInformation(
                    "⚙️ **Configuration Update**\n✅ No changes detected - configuration already up to date");
            }

            return true;
        }
        catch (Exception ex)
        {
            await LogWarning($"Failed to update bot configuration: {ex.Message}");
            return false;
        }
    }

    /// <summary>
    /// Gets the current trading bot configuration.
    /// </summary>
    /// <returns>A copy of the current configuration</returns>
    public TradingBotConfig GetConfiguration()
    {
        return new TradingBotConfig
        {
            AccountName = Config.AccountName,
            MoneyManagement = Config.MoneyManagement,
            Ticker = Config.Ticker,
            ScenarioName = Config.ScenarioName,
            Scenario = Config.Scenario,
            Timeframe = Config.Timeframe,
            IsForWatchingOnly = Config.IsForWatchingOnly,
            BotTradingBalance = Config.BotTradingBalance,
            IsForBacktest = Config.IsForBacktest,
            CooldownPeriod = Config.CooldownPeriod,
            MaxLossStreak = Config.MaxLossStreak,
            MaxPositionTimeHours = Config.MaxPositionTimeHours,
            FlipOnlyWhenInProfit = Config.FlipOnlyWhenInProfit,
            FlipPosition = Config.FlipPosition,
            Name = Config.Name,
            CloseEarlyWhenProfitable = Config.CloseEarlyWhenProfitable,
            UseSynthApi = Config.UseSynthApi,
            UseForPositionSizing = Config.UseForPositionSizing,
            UseForSignalFiltering = Config.UseForSignalFiltering,
            UseForDynamicStopLoss = Config.UseForDynamicStopLoss,
            RiskManagement = Config.RiskManagement,
        };
    }


    /// <summary>
    /// Checks if the bot is currently in a cooldown period for any direction.
    /// </summary>
    /// <returns>True if in cooldown period for any direction, false otherwise</returns>
    private bool IsInCooldownPeriod()
    {
        if (LastPositionClosingTime == null)
        {
            return false; // No previous position closing time, no cooldown
        }

        // Calculate cooldown end time based on last position closing time
        var baseIntervalSeconds = CandleExtensions.GetBaseIntervalInSeconds(Config.Timeframe);
        var cooldownEndTime = LastPositionClosingTime.Value.AddSeconds(baseIntervalSeconds * Config.CooldownPeriod);
        var isInCooldown = LastCandle.Date < cooldownEndTime;

        if (isInCooldown)
        {
            var remainingTime = cooldownEndTime - LastCandle.Date;

            Logger.LogWarning(
                $"⏳ **Cooldown Period Active**\n" +
                $"Cannot open new positions\n" +
                $"Last position closed: `{LastPositionClosingTime:HH:mm:ss}`\n" +
                $"Cooldown period: `{Config.CooldownPeriod}` candles\n" +
                $"Cooldown ends: `{cooldownEndTime:HH:mm:ss}`\n" +
                $"Remaining time: `{remainingTime.TotalMinutes:F1} minutes`");
        }

        return isInCooldown;
    }
}