using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Trading;
using Managing.Application.Trading.Commands;
using Managing.Common;
using Managing.Core.FixedSizedQueue;
using Managing.Domain.Accounts;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Scenarios;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Trades;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using static Managing.Common.Enums;

namespace Managing.Application.Bots;

public class TradingBot : Bot, ITradingBot
{
    public readonly ILogger<TradingBot> Logger;
    public readonly IExchangeService ExchangeService;
    public readonly IMessengerService MessengerService;
    public readonly IAccountService AccountService;
    private readonly ITradingService TradingService;
    private readonly IBotService BotService;

    public TradingBotConfig Config { get; set; }
    public Account Account { get; set; }
    public HashSet<IStrategy> Strategies { get; set; }
    public FixedSizeQueue<Candle> OptimizedCandles { get; set; }
    public HashSet<Candle> Candles { get; set; }
    public HashSet<Signal> Signals { get; set; }
    public List<Position> Positions { get; set; }
    public Dictionary<DateTime, decimal> WalletBalances { get; set; }
    public Dictionary<StrategyType, StrategiesResultBase> StrategiesValues { get; set; }
    public DateTime StartupTime { get; set; }
    public DateTime PreloadSince { get; set; }
    public int PreloadedCandlesCount { get; set; }
    public decimal Fee { get; set; }
    public Scenario Scenario { get; set; }


    public TradingBot(
        IExchangeService exchangeService,
        ILogger<TradingBot> logger,
        ITradingService tradingService,
        IAccountService accountService,
        IMessengerService messengerService,
        IBotService botService,
        TradingBotConfig config
    )
        : base(config.Name)
    {
        ExchangeService = exchangeService;
        AccountService = accountService;
        MessengerService = messengerService;
        TradingService = tradingService;
        BotService = botService;
        Logger = logger;

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

        Config = config;

        Strategies = new HashSet<IStrategy>();
        Signals = new HashSet<Signal>();
        OptimizedCandles = new FixedSizeQueue<Candle>(600);
        Candles = new HashSet<Candle>();
        Positions = new List<Position>();
        WalletBalances = new Dictionary<DateTime, decimal>();
        StrategiesValues = new Dictionary<StrategyType, StrategiesResultBase>();

        if (!Config.IsForBacktest)
        {
            Interval = CandleExtensions.GetIntervalFromTimeframe(Config.Timeframe);
            PreloadSince = CandleExtensions.GetBotPreloadSinceFromTimeframe(Config.Timeframe);
        }
    }

    public override void Start()
    {
        base.Start();
        // Load account synchronously
        LoadAccount().GetAwaiter().GetResult();

        if (!Config.IsForBacktest)
        {
            LoadScenario(Config.ScenarioName);
            PreloadCandles().GetAwaiter().GetResult();
            CancelAllOrders().GetAwaiter().GetResult();

            try
            {
                // await MessengerService.SendMessage(
                //     $"Hey everyone! I'm about to start {Name}. 🚀\n" +
                //     $"I'll post an update here each time a signal is triggered by the following strategies: {string.Join(", ", Strategies.Select(s => s.Name))}."
                // );
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, ex.Message);
            }

            InitWorker(Run).GetAwaiter().GetResult();
        }

        // Fee = TradingService.GetFee(Account, IsForBacktest);
    }

    public async Task LoadAccount()
    {
        var account = await AccountService.GetAccount(Config.AccountName, false, false);
        if (account == null)
        {
            Logger.LogWarning($"No account found for this {Config.AccountName}");
            Stop();
        }
        else
        {
            Account = account;
        }
    }

    public void LoadScenario(string scenarioName)
    {
        var scenario = TradingService.GetScenarioByName(scenarioName);
        if (scenario == null)
        {
            Logger.LogWarning("No scenario found for this scenario name");
            Stop();
        }
        else
        {
            Scenario = scenario;
            LoadStrategies(ScenarioHelpers.GetStrategiesFromScenario(scenario));
        }
    }

    public void LoadStrategies(IEnumerable<IStrategy> strategies)
    {
        foreach (var strategy in strategies)
        {
            Strategies.Add(strategy);
        }
    }

    public async Task Run()
    {
        if (!Config.IsForBacktest)
        {
            // Check broker balance before running
            var balance = await ExchangeService.GetBalance(Account, false);
            if (balance < Constants.GMX.Config.MinimumPositionAmount)
            {
                await LogWarning(
                    $"Balance on broker is below {Constants.GMX.Config.MinimumPositionAmount} USD (actual: {balance}). Stopping bot {Identifier} and saving backup.");
                SaveBackup();
                Stop();
                return;
            }

            Logger.LogInformation($"____________________{Name}____________________");
            Logger.LogInformation(
                $"Time : {DateTime.Now} - Server time {DateTime.Now.ToUniversalTime()} - Last candle : {OptimizedCandles.Last().Date} - Bot : {Name} - Type {Config.BotType} - Ticker : {Config.Ticker}");
        }

        var previousLastCandle = OptimizedCandles.LastOrDefault();

        if (!Config.IsForBacktest)
            await UpdateCandles();

        var currentLastCandle = OptimizedCandles.LastOrDefault();

        if (currentLastCandle != previousLastCandle || Config.IsForBacktest)
            await UpdateSignals(OptimizedCandles);
        else
            Logger.LogInformation($"No need to update signals for {Config.Ticker}");

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

        if (!Config.IsForBacktest)
        {
            SaveBackup();
            UpdateStrategiesValues();
        }

        UpdateWalletBalances();
        if (OptimizedCandles.Count % 100 == 0) // Log every 10th execution
        {
            Logger.LogInformation($"Candle date : {OptimizedCandles.Last().Date:u}");
            Logger.LogInformation($"Signals : {Signals.Count}");
            Logger.LogInformation($"ExecutionCount : {ExecutionCount}");
            Logger.LogInformation($"Positions : {Positions.Count}");
            Logger.LogInformation("__________________________________________________");
        }
    }

    public void UpdateStrategiesValues()
    {
        foreach (var strategy in Strategies)
        {
            StrategiesValues[strategy.Type] = ((Strategy)strategy).GetStrategyValues();
        }
    }

    private async Task PreloadCandles()
    {
        if (OptimizedCandles.Any())
            return;

        var haveSignal = Signals.Any();
        if (haveSignal)
        {
            PreloadSince = Signals.First().Date;
        }

        var candles =
            await ExchangeService.GetCandlesInflux(Account.Exchange, Config.Ticker, PreloadSince, Config.Timeframe);

        foreach (var candle in candles.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
        {
            if (!OptimizedCandles.Any(c => c.Date == candle.Date))
            {
                OptimizedCandles.Enqueue(candle);
                Candles.Add(candle);

                if (!haveSignal)
                {
                    await UpdateSignals(OptimizedCandles);
                }
            }
        }

        PreloadedCandlesCount = OptimizedCandles.Count();
    }

    private async Task UpdateSignals(FixedSizeQueue<Candle> candles)
    {
        var signal = TradingBox.GetSignal(candles.ToHashSet(), Strategies, Signals, Scenario.LoopbackPeriod);
        if (signal == null) return;

        signal.User = Account.User;
        await AddSignal(signal);
    }

    private async Task AddSignal(Signal signal)
    {
        if (Config.IsForWatchingOnly || (ExecutionCount < 1 && !Config.IsForBacktest))
            signal.Status = SignalStatus.Expired;

        Signals.Add(signal);

        var signalText = $"{Config.ScenarioName} trigger a signal. Signal told you " +
                         $"to {signal.Direction} {Config.Ticker} on {Config.Timeframe}. The confidence in this signal is {signal.Confidence}. Identifier : {signal.Identifier}";

        Logger.LogInformation(signalText);

        if (Config.IsForWatchingOnly && !Config.IsForBacktest && ExecutionCount > 0)
        {
            await MessengerService.SendSignal(signalText, Account.Exchange, Config.Ticker, signal.Direction,
                Config.Timeframe);
        }
    }

    protected async Task UpdateCandles()
    {
        if (OptimizedCandles.Count == 0 || ExecutionCount == 0)
            return;

        var lastCandle = OptimizedCandles.Last();
        var newCandle =
            await ExchangeService.GetCandlesInflux(Account.Exchange, Config.Ticker, lastCandle.Date, Config.Timeframe);

        foreach (var candle in newCandle.Where(c => c.Date < DateTime.Now.ToUniversalTime()))
        {
            OptimizedCandles.Enqueue(candle);
            Candles.Add(candle);
        }
    }

    private async Task<Signal> RecreateSignalFromPosition(Position position)
    {
        try
        {
            // Get the candle that corresponds to the position opening time
            var positionCandle = OptimizedCandles.FirstOrDefault(c => c.Date <= position.Open.Date)
                                 ?? OptimizedCandles.LastOrDefault();

            if (positionCandle == null)
            {
                await LogWarning(
                    $"Cannot find candle for position {position.Identifier} opened at {position.Open.Date}");
                return null;
            }

            // Create a new signal based on position information
            var recreatedSignal = new Signal(
                ticker: Config.Ticker,
                direction: position.OriginDirection,
                confidence: Confidence.Medium, // Default confidence for recreated signals
                candle: positionCandle,
                date: position.Open.Date,
                exchange: Account.Exchange,
                strategyType: StrategyType.Stc, // Use a valid strategy type for recreated signals
                signalType: SignalType.Signal
            );

            // 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;
            recreatedSignal.User = Account.User;

            // Add the recreated signal to our collection
            Signals.Add(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()
    {
        foreach (var position in Positions.Where(p => !p.IsFinished()))
        {
            var signalForPosition = Signals.FirstOrDefault(s => s.Identifier == 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);
        }

        // Open position for signals waiting for a position open
        foreach (var signal in Signals.Where(s => s.Status == SignalStatus.WaitingForPosition))
        {
            Task.Run(() => OpenPosition(signal)).GetAwaiter().GetResult();
        }
    }

    private void UpdateWalletBalances()
    {
        var lastCandle = OptimizedCandles.LastOrDefault();
        if (lastCandle == null) return;

        var date = lastCandle.Date;

        if (WalletBalances.Count == 0)
        {
            // WalletBalances[date] = await ExchangeService.GetBalance(Account, IsForBacktest);
            WalletBalances[date] = Config.BotTradingBalance;
            return;
        }

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

    private async Task UpdatePosition(Signal signal, Position positionForSignal)
    {
        try
        {
            Logger.LogInformation($"📊 **Position Update**\nUpdating position: `{positionForSignal.SignalIdentifier}`");

            var position = Config.IsForBacktest
                ? positionForSignal
                : TradingService.GetPositionByIdentifier(positionForSignal.Identifier);

            var positionsExchange = Config.IsForBacktest
                ? new List<Position> { position }
                : await TradingService.GetBrokerPositions(Account);

            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);
                    }

                    position = brokerPosition;
                }
                else
                {
                    // No position, position close on the broker 
                    if (!position.Status.Equals(PositionStatus.New))
                    {
                        // Setup the previous status of the position
                        position.Status = PositionStatus.Filled;
                    }
                }
            }

            if (position.Status == PositionStatus.New)
            {
                var orders = await ExchangeService.GetOpenOrders(Account, Config.Ticker);
                if (orders.Any())
                {
                    // If there are 3 or more orders and position is still not filled, check if enough time has passed
                    if (orders.Count() >= 3)
                    {
                        var currentTime = Config.IsForBacktest ? OptimizedCandles.Last().Date : 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 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 Not Found**\nNo position on exchange and no orders\nSignal: `{signal.Identifier}`\nPosition might be already closed");
                    await HandleClosedPosition(positionForSignal);
                }
            }
            else if (position.Status == (PositionStatus.Finished | PositionStatus.Flipped))
            {
                await HandleClosedPosition(positionForSignal);
            }
            else if (position.Status == (PositionStatus.Filled | PositionStatus.PartiallyFilled))
            {
                // For backtesting or force close if not executed on exchange : 
                // check if position is still open
                // Check status, if still open update the status of the position

                // Position might be partially filled, meaning that TPSL havent been sended yet
                // But the position might already been closed by the exchange so we have to check should be closed
                var lastCandle = Config.IsForBacktest
                    ? OptimizedCandles.Last()
                    : 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;

                // Check if position is in profit by comparing entry price with current market price
                var isPositionInProfit = positionForSignal.OriginDirection == TradeDirection.Long
                    ? lastCandle.Close > positionForSignal.Open.Price
                    : lastCandle.Close < positionForSignal.Open.Price;

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

                // 2. Time-based closure (if time limit exceeded)
                if (hasExceededTimeLimit)
                {
                    // If CloseEarlyWhenProfitable is enabled, only close if profitable
                    // If CloseEarlyWhenProfitable is disabled, close regardless of profit status
                    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;
                    }
                    else
                    {
                        await LogInformation(
                            $"⏳ **Time Limit - Waiting**\nTime limit exceeded but position at loss\n📉 Entry: `${positionForSignal.Open.Price}` → Current: `${lastCandle.Close}`\n💰 Realized PNL: `${currentPnl:F2}` (`{pnlPercentage:F2}%`\n🎯 Waiting for profit before closing (CloseEarlyWhenProfitable enabled)");
                    }
                }

                // 3. Normal stop loss and take profit checks
                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 | PositionStatus.Canceled))
            {
                await LogWarning($"Open position trade is rejected for signal {signal.Identifier}");
                // if position is not open
                // Re-open the trade for the signal only if signal still up 
                if (signal.Status == SignalStatus.PositionOpen)
                {
                    Logger.LogInformation($"Try to re-open position");
                    await OpenPosition(signal);
                }
            }
        }
        catch (Exception ex)
        {
            await LogWarning($"Cannot update position {positionForSignal.Identifier}: {ex.Message}");
            SentrySdk.CaptureException(ex);
            return;
        }
    }

    private async Task OpenPosition(Signal signal)
    {
        // Check if a position is already open
        Logger.LogInformation($"Opening position for {signal.Identifier}");

        var openedPosition = Positions.FirstOrDefault(p => p.Status == PositionStatus.Filled
                                                           && p.SignalIdentifier != signal.Identifier);

        var lastPrice = Config.IsForBacktest
            ? OptimizedCandles.Last().Close
            : ExchangeService.GetPrice(Account, Config.Ticker, DateTime.UtcNow);

        // If position open
        if (openedPosition != null)
        {
            var previousSignal = Signals.First(s => s.Identifier == openedPosition.SignalIdentifier);

            // Check if signal is the opposite side => flip the position
            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);
            }
            else
            {
                // An operation is already open for the opposite direction
                // ==> Flip the position
                if (Config.FlipPosition)
                {
                    // Check if current position is in profit before flipping
                    var isPositionInProfit = (openedPosition.ProfitAndLoss?.Realized ?? 0) > 0;

                    // Determine if we should flip based on configuration
                    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);
                        await OpenPosition(signal);
                        await LogInformation(
                            $"✅ **Position Flipped**\nPosition: `{previousSignal.Identifier}` → `{signal.Identifier}`\nPrice: `${lastPrice}`");
                    }
                    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;
                    }
                }
                else
                {
                    await LogInformation(
                        $"🚫 **Flip Disabled**\nPosition already open for: `{previousSignal.Identifier}`\nFlipping disabled, new signal expired");
                    SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                }
            }
        }
        else
        {
            if (!(await CanOpenPosition(signal)))
            {
                SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                return;
            }

            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,
                    User,
                    Config.BotTradingBalance,
                    Config.IsForBacktest,
                    lastPrice,
                    signalIdentifier: signal.Identifier);

                var position = (new OpenPositionCommandHandler(ExchangeService, AccountService, TradingService)
                    .Handle(command)).Result;

                if (position != null)
                {
                    Positions.Add(position);

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

                        if (!Config.IsForBacktest)
                        {
                            await MessengerService.SendPosition(position);
                        }

                        Logger.LogInformation($"Position requested");
                    }
                    else
                    {
                        await SetPositionStatus(signal.Identifier, PositionStatus.Rejected);
                        SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                    }
                }
            }
            catch (Exception ex)
            {
                // Keep signal open for debug purpose 
                //SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                SetSignalStatus(signal.Identifier, SignalStatus.Expired);
                await LogWarning($"Cannot open trade : {ex.Message}, stackTrace : {ex.StackTrace}");
            }
        }
    }

    private async Task<bool> CanOpenPosition(Signal signal)
    {
        // Early return if we're in backtest mode and haven't executed yet
        if (!Config.IsForBacktest && ExecutionCount < 1)
        {
            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 await CheckCooldownPeriod(signal) && await CheckLossStreak(signal);
        }

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

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

    private async Task<bool> CheckLossStreak(Signal 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
            .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
        {
            var positions = await ExchangeService.GetBrokerPositions(Account);
            if (!positions.Any(p => p.Ticker == Config.Ticker))
            {
                return true;
            }

            // Handle existing position on broker
            var previousPosition = Positions.LastOrDefault();
            var orders = await ExchangeService.GetOpenOrders(Account, Config.Ticker);

            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;
        }
    }

    private async Task<bool> CheckCooldownPeriod(Signal signal)
    {
        var lastPosition = Positions.LastOrDefault(p => p.IsFinished()
                                                        && p.SignalIdentifier != signal.Identifier
                                                        && p.OriginDirection == signal.Direction);

        if (lastPosition == null)
        {
            return true;
        }

        var cooldownCandle = OptimizedCandles.TakeLast((int)Config.CooldownPeriod).FirstOrDefault();
        if (cooldownCandle == null)
        {
            await LogWarning("📊 **Cooldown Check Failed**\nCannot check cooldown\nNot enough candles available");
            return false;
        }

        var positionSignal = Signals.FirstOrDefault(s => s.Identifier == lastPosition.SignalIdentifier);
        if (positionSignal == null)
        {
            await LogWarning($"Cannot find signal for last position {lastPosition.Identifier}");
            return false;
        }

        var canOpenPosition = positionSignal.Date < cooldownCandle.Date;
        if (!canOpenPosition)
        {
            await LogInformation(
                $"⏳ **Cooldown Active**\nPosition blocked by cooldown period\n📅 Last Position: `{positionSignal.Date:MM/dd HH:mm}`\n🕒 Cooldown Until: `{cooldownCandle.Date:MM/dd HH:mm}`");
        }

        return canOpenPosition;
    }

    public async Task CloseTrade(Signal 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")}`");

        // Get status of position before closing it. The position might be already close by the exchange
        if (!Config.IsForBacktest && await ExchangeService.GetQuantityInPosition(Account, Config.Ticker) == 0)
        {
            Logger.LogInformation($"Trade already close on exchange");
            await HandleClosedPosition(position);
        }
        else
        {
            var command = new ClosePositionCommand(position, lastPrice, isForBacktest: Config.IsForBacktest);
            try
            {
                var closedPosition = (new ClosePositionCommandHandler(ExchangeService, AccountService, TradingService)
                    .Handle(command)).Result;

                if (closedPosition.Status == (PositionStatus.Finished | 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 | PositionStatus.Rejected))
                {
                    // Trade close on exchange => Should close trade manually
                    await SetPositionStatus(signal.Identifier, PositionStatus.Finished);
                }
            }
        }
    }

    private async Task HandleClosedPosition(Position position)
    {
        if (Positions.Any(p => p.Identifier == position.Identifier))
        {
            // Update the close date for the trade that actually closed the position
            var currentCandle = Config.IsForBacktest
                ? OptimizedCandles.LastOrDefault()
                : ExchangeService.GetCandle(Account, Config.Ticker, DateTime.UtcNow);

            if (currentCandle != null && position.ProfitAndLoss != null)
            {
                // Determine which trade closed the position based on realized P&L
                if (position.ProfitAndLoss.Realized > 0)
                {
                    // Profitable close = Take Profit
                    position.TakeProfit1.SetDate(currentCandle.Date);
                }
                else
                {
                    // Loss or breakeven close = Stop Loss
                    position.StopLoss.SetDate(currentCandle.Date);
                }
            }

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

            // Update the bot's trading balance after position is closed
            if (position.ProfitAndLoss != null)
            {
                // Add PnL (could be positive or negative)
                Config.BotTradingBalance += position.ProfitAndLoss.Realized;

                // Subtract fees
                Config.BotTradingBalance -= GetPositionFees(position);

                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 MessengerService.SendClosingPosition(position);
        }

        await CancelAllOrders();
    }

    private async Task CancelAllOrders()
    {
        if (!Config.IsForBacktest && !Config.IsForWatchingOnly)
        {
            try
            {
                var openOrders = await ExchangeService.GetOpenOrders(Account, Config.Ticker);
                if (openOrders.Any())
                {
                    var openPositions = (await ExchangeService.GetBrokerPositions(Account))
                        .Where(p => p.Ticker == Config.Ticker);
                    var cancelClose = openPositions.Any();

                    if (cancelClose)
                    {
                        Logger.LogInformation($"Position still open, cancel close orders");
                    }
                    else
                    {
                        Logger.LogInformation($"Canceling all orders for {Config.Ticker}");
                        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)
    {
        var position = Positions.First(p => p.SignalIdentifier == signalIdentifier);
        if (!position.Status.Equals(positionStatus))
        {
            Positions.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);
    }

    private void UpdatePositionPnl(string identifier, decimal realized)
    {
        Positions.First(p => p.Identifier == identifier).ProfitAndLoss = new ProfitAndLoss()
        {
            Realized = realized
        };
    }

    private void SetSignalStatus(string signalIdentifier, SignalStatus signalStatus)
    {
        if (Signals.Any(s => s.Identifier == signalIdentifier && s.Status != signalStatus))
        {
            Signals.First(s => s.Identifier == signalIdentifier).Status = signalStatus;
            Logger.LogInformation($"Signal {signalIdentifier} is now {signalStatus}");
        }
    }

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

        if (total == 0)
            return 0;

        return (succeededPositions * 100) / total;
    }

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

    /// <summary>
    /// Calculates the total fees paid by the trading bot for each position.
    /// </summary>
    /// <returns>Returns the total fees paid as a decimal value.</returns>
    public decimal GetTotalFees()
    {
        decimal fees = 0;
        foreach (var position in Positions.Where(p => p.Open.Fee > 0))
        {
            fees += position.Open.Fee;
            fees += position.StopLoss.Status == TradeStatus.Filled ? position.StopLoss.Fee : 0;
            fees += position.TakeProfit1.Status == TradeStatus.Filled ? position.TakeProfit1.Fee : 0;

            if (position.IsFinished() &&
                position.StopLoss.Status != TradeStatus.Filled && position.TakeProfit1.Status != TradeStatus.Filled)
                fees += position.Open.Fee;

            if (position.TakeProfit2 != null)
                fees += position.TakeProfit2.Fee;
        }

        return fees;
    }

    /// <summary>
    /// Calculates the total fees for a specific position
    /// </summary>
    /// <param name="position">The position to calculate fees for</param>
    /// <returns>The total fees for the position</returns>
    private decimal GetPositionFees(Position position)
    {
        decimal fees = 0;

        fees += position.Open.Fee;
        fees += position.StopLoss.Status == TradeStatus.Filled ? position.StopLoss.Fee : 0;
        fees += position.TakeProfit1.Status == TradeStatus.Filled ? position.TakeProfit1.Fee : 0;

        if (position.IsFinished() &&
            position.StopLoss.Status != TradeStatus.Filled && position.TakeProfit1.Status != TradeStatus.Filled)
            fees += position.Open.Fee;

        if (position.TakeProfit2 != null)
            fees += position.TakeProfit2.Status == TradeStatus.Filled ? position.TakeProfit2.Fee : 0;

        return fees;
    }

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

    private async Task LogInformation(string message)
    {
        Logger.LogInformation(message);
        try
        {
            await SendTradeMessage(message);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }

    private async Task LogWarning(string message)
    {
        message = $"[{Identifier}] {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)
        {
            // Add bot name at the top of every message
            var messageWithBotName = $"🤖 **{Name}**\n{message}";
            await MessengerService.SendTradeMessage(messageWithBotName, isBadBehavior, Account?.User);
        }
    }

    public override void SaveBackup()
    {
        var data = new TradingBotBackup
        {
            Name = Name,
            BotType = Config.BotType,
            Signals = Signals,
            Positions = Positions,
            Timeframe = Config.Timeframe,
            Ticker = Config.Ticker,
            ScenarioName = Config.ScenarioName,
            AccountName = Config.AccountName,
            IsForWatchingOnly = Config.IsForWatchingOnly,
            WalletBalances = WalletBalances,
            MoneyManagement = Config.MoneyManagement,
            BotTradingBalance = Config.BotTradingBalance,
            StartupTime = StartupTime,
            CooldownPeriod = Config.CooldownPeriod,
            MaxLossStreak = Config.MaxLossStreak,
            MaxPositionTimeHours = Config.MaxPositionTimeHours ?? 0m,
            FlipOnlyWhenInProfit = Config.FlipOnlyWhenInProfit,
            CloseEarlyWhenProfitable = Config.CloseEarlyWhenProfitable,
        };
        BotService.SaveOrUpdateBotBackup(User, Identifier, Config.BotType, Status, JsonConvert.SerializeObject(data));
    }

    public override void LoadBackup(BotBackup backup)
    {
        var data = JsonConvert.DeserializeObject<TradingBotBackup>(backup.Data);
        Config = new TradingBotConfig
        {
            AccountName = data.AccountName,
            MoneyManagement = data.MoneyManagement,
            Ticker = data.Ticker,
            ScenarioName = data.ScenarioName,
            Timeframe = data.Timeframe,
            IsForBacktest = false, // Always false when loading from backup
            IsForWatchingOnly = data.IsForWatchingOnly,
            BotTradingBalance = data.BotTradingBalance,
            BotType = data.BotType,
            CooldownPeriod = data.CooldownPeriod,
            MaxLossStreak = data.MaxLossStreak,
            MaxPositionTimeHours = data.MaxPositionTimeHours == 0m ? null : data.MaxPositionTimeHours,
            FlipOnlyWhenInProfit = data.FlipOnlyWhenInProfit,
            CloseEarlyWhenProfitable = data.CloseEarlyWhenProfitable,
            Name = data.Name
        };

        Signals = data.Signals;
        Positions = data.Positions;
        WalletBalances = data.WalletBalances;
        PreloadSince = data.StartupTime;
        Identifier = backup.Identifier;
        User = backup.User;
        Status = backup.LastStatus;
    }

    /// <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)
    {
        var lastCandle = OptimizedCandles.LastOrDefault();
        if (lastCandle == null)
        {
            throw new Exception("No candles available to open position");
        }

        // Create a fake signal for manual position opening
        var signal = new Signal(Config.Ticker, direction, Confidence.Low, lastCandle, lastCandle.Date,
            TradingExchanges.GmxV2,
            StrategyType.Stc, SignalType.Signal);
        signal.Status = SignalStatus.WaitingForPosition; // Ensure status is correct
        signal.User = Account.User; // Assign user

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

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

        // Get the opened position
        var position = Positions.FirstOrDefault(p => p.SignalIdentifier == signal.Identifier);
        if (position == null)
        {
            // Clean up the signal if position creation failed
            SetSignalStatus(signal.Identifier, SignalStatus.Expired);
            throw new Exception("Failed to open position");
        }

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

    /// <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)
        {
            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.
    /// </summary>
    /// <param name="newConfig">The new configuration to apply</param>
    /// <returns>True if the configuration was successfully updated, false otherwise</returns>
    public async Task<bool> UpdateConfiguration(TradingBotConfig newConfig)
    {
        return await UpdateConfiguration(newConfig, allowNameChange: false);
    }

    /// <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>
    /// <param name="allowNameChange">Whether to allow changing the bot name/identifier</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, bool allowNameChange = false)
    {
        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 (string.IsNullOrEmpty(newConfig.ScenarioName))
            {
                throw new ArgumentException("Scenario name cannot be null or empty");
            }

            // Protect critical properties that shouldn't change for running bots
            var protectedBotType = Config.BotType;
            var protectedIsForBacktest = Config.IsForBacktest;
            var protectedName = allowNameChange ? newConfig.Name : Config.Name;

            // Log the configuration update (before changing anything)
            await LogInformation("⚙️ **Configuration Update**\n" +
                                 "📊 **Previous Settings:**\n" +
                                 $"💰 Balance: ${Config.BotTradingBalance:F2}\n" +
                                 $"⏱️ Max Time: {(Config.MaxPositionTimeHours?.ToString() + "h" ?? "Disabled")}\n" +
                                 $"📈 Flip Only in Profit: {(Config.FlipOnlyWhenInProfit ? "✅" : "❌")}\n" +
                                 $"⏳ Cooldown: {Config.CooldownPeriod} candles\n" +
                                 $"📉 Max Loss Streak: {Config.MaxLossStreak}" +
                                 (allowNameChange && newConfig.Name != Config.Name ? $"\n🏷️ Name: {Config.Name} → {newConfig.Name}" : ""));

            // Update the configuration
            Config = newConfig;

            // Restore protected properties
            Config.BotType = protectedBotType;
            Config.IsForBacktest = protectedIsForBacktest;
            Config.Name = protectedName;

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

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

            // If scenario changed, reload it
            var currentScenario = Scenario?.Name;
            if (Config.ScenarioName != currentScenario)
            {
                LoadScenario(Config.ScenarioName);
            }

            await LogInformation("✅ **Configuration Applied**\n" +
                                 "🔧 **New Settings:**\n" +
                                 $"💰 Balance: ${Config.BotTradingBalance:F2}\n" +
                                 $"⏱️ Max Time: {(Config.MaxPositionTimeHours?.ToString() + "h" ?? "Disabled")}\n" +
                                 $"📈 Flip Only in Profit: {(Config.FlipOnlyWhenInProfit ? "✅" : "❌")}\n" +
                                 $"⏳ Cooldown: {Config.CooldownPeriod} candles\n" +
                                 $"📉 Max Loss Streak: {Config.MaxLossStreak}");

            // Save the updated configuration as backup
            if (!Config.IsForBacktest)
            {
                SaveBackup();
            }

            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,
            Timeframe = Config.Timeframe,
            IsForWatchingOnly = Config.IsForWatchingOnly,
            BotTradingBalance = Config.BotTradingBalance,
            BotType = Config.BotType,
            IsForBacktest = Config.IsForBacktest,
            CooldownPeriod = Config.CooldownPeriod,
            MaxLossStreak = Config.MaxLossStreak,
            MaxPositionTimeHours = Config.MaxPositionTimeHours,
            FlipOnlyWhenInProfit = Config.FlipOnlyWhenInProfit,
            FlipPosition = Config.FlipPosition,
            Name = Config.Name,
            CloseEarlyWhenProfitable = Config.CloseEarlyWhenProfitable
        };
    }
}

public class TradingBotBackup
{
    public string Name { get; set; }
    public BotType BotType { get; set; }
    public HashSet<Signal> Signals { get; set; }
    public List<Position> Positions { get; set; }
    public Timeframe Timeframe { get; set; }
    public Ticker Ticker { get; set; }
    public string ScenarioName { get; set; }
    public string AccountName { get; set; }
    public bool IsForWatchingOnly { get; set; }
    public Dictionary<DateTime, decimal> WalletBalances { get; set; }
    public MoneyManagement MoneyManagement { get; set; }
    public DateTime StartupTime { get; set; }
    public decimal BotTradingBalance { get; set; }
    public int CooldownPeriod { get; set; }
    public int MaxLossStreak { get; set; }
    public decimal MaxPositionTimeHours { get; set; }
    public bool FlipOnlyWhenInProfit { get; set; }
    public bool CloseEarlyWhenProfitable { get; set; }
}