using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Common;
using Managing.Core;
using Managing.Domain.Accounts;
using Managing.Domain.Bots;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Trades;
using Managing.Domain.Users;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;

namespace Managing.Application.Bots.Grains;

/// <summary>
/// Orleans grain for live trading bot operations.
/// Uses composition with TradingBotBase to maintain separation of concerns.
/// This grain handles live trading scenarios with real-time market data and execution.
/// </summary>
public class LiveTradingBotGrain : Grain, ILiveTradingBotGrain, IRemindable
{
    private readonly IPersistentState<TradingBotGrainState> _state;
    private readonly ILogger<LiveTradingBotGrain> _logger;
    private readonly IServiceScopeFactory _scopeFactory;
    private TradingBotBase? _tradingBot;
    private IDisposable? _timer;
    private string _reminderName = "RebootReminder";

    public LiveTradingBotGrain(
        [PersistentState("live-trading-bot", "bot-store")]
        IPersistentState<TradingBotGrainState> state,
        ILogger<LiveTradingBotGrain> logger,
        IServiceScopeFactory scopeFactory)
    {
        _logger = logger;
        _scopeFactory = scopeFactory;
        _state = state;
    }

    public override async Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("LiveTradingBotGrain {GrainId} activated", this.GetPrimaryKey());
        await base.OnActivateAsync(cancellationToken);
        await ResumeBotIfRequiredAsync();
    }

    public override async Task OnDeactivateAsync(DeactivationReason reason, CancellationToken cancellationToken)
    {
        _logger.LogInformation("LiveTradingBotGrain {GrainId} deactivating. Reason: {Reason}",
            this.GetPrimaryKey(), reason.Description);

        StopAndDisposeTimer();
        await base.OnDeactivateAsync(reason, cancellationToken);
    }

    public async Task CreateAsync(TradingBotConfig config, User user)
    {
        if (config == null || string.IsNullOrEmpty(config.Name))
        {
            throw new InvalidOperationException("Bot configuration is not properly initialized");
        }

        if (config.IsForBacktest)
        {
            throw new InvalidOperationException("LiveTradingBotGrain cannot be used for backtesting");
        }

        // This is a new bot, so we can assume it's not registered or active.
        _state.State.Config = config;
        _state.State.User = user;
        _state.State.CreateDate = DateTime.UtcNow;
        _state.State.Identifier = this.GetPrimaryKey();
        await _state.WriteStateAsync();

        var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
        await botRegistry.RegisterBot(_state.State.Identifier, user.Id);

        // Register the bot with the user's agent
        var agentGrain = GrainFactory.GetGrain<IAgentGrain>(user.Id);
        await agentGrain.RegisterBotAsync(_state.State.Identifier);

        await SaveBotAsync(BotStatus.Saved);

        _logger.LogInformation("LiveTradingBotGrain {GrainId} created successfully", this.GetPrimaryKey());
    }

    private async Task ResumeBotIfRequiredAsync()
    {
        // Make the network call to the registry to get the source of truth
        var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
        var botId = this.GetPrimaryKey();
        var botStatus = await botRegistry.GetBotStatus(botId);

        _logger.LogInformation("LiveTradingBotGrain {GrainId} activated. Registry status: {Status}",
            botId, botStatus);

        if (botStatus == BotStatus.Running && _tradingBot == null)
        {
            // Now, we can proceed with resuming the bot.
            await ResumeBotInternalAsync(botStatus);
        }
    }

    private async Task ResumeBotInternalAsync(BotStatus previousStatus)
    {
        // Idempotency check
        if (_tradingBot != null)
        {
            return;
        }

        try
        {
            // Create and initialize trading bot instance
            _tradingBot = CreateTradingBotInstance(_state.State.Config);
            await _tradingBot.Start(previousStatus);

            // Set startup time when bot actually starts running
            _state.State.StartupTime = DateTime.UtcNow;
            await _state.WriteStateAsync();
            
            // Start the in-memory timer and persistent reminder
            RegisterAndStartTimer();
            await RegisterReminder();
            
            // Update both database and registry status
            await SaveBotAsync(BotStatus.Running);
            await UpdateBotRegistryStatus(BotStatus.Running);
            
            _logger.LogInformation("LiveTradingBotGrain {GrainId} resumed successfully", this.GetPrimaryKey());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to resume bot {GrainId}", this.GetPrimaryKey());
            _tradingBot = null; // Clean up on failure
            await UpdateBotRegistryStatus(BotStatus.Stopped);
            throw;
        }
    }

    public async Task StartAsync()
    {
        var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
        var botId = this.GetPrimaryKey();
        var status = await botRegistry.GetBotStatus(botId);

        // Check if already running
        if (status == BotStatus.Running && _tradingBot != null)
        {
            await RegisterReminder();
            _logger.LogInformation("LiveTradingBotGrain {GrainId} is already running", this.GetPrimaryKey());
            return;
        }

        try
        {
            // Resume the bot - this handles registry status update internally
            await ResumeBotInternalAsync(status);
            _logger.LogInformation("LiveTradingBotGrain {GrainId} started successfully", this.GetPrimaryKey());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to start LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
            // Ensure registry status is correct on failure
            await UpdateBotRegistryStatus(BotStatus.Stopped);
            throw;
        }
    }

    private async Task RegisterReminder()
    {
        var reminderPeriod = TimeSpan.FromMinutes(2);
        await this.RegisterOrUpdateReminder(_reminderName, reminderPeriod, reminderPeriod);
    }

    /// <summary>
    /// Starts the Orleans timer for periodic bot execution
    /// </summary>
    private void RegisterAndStartTimer()
    {
        if (_tradingBot == null) return;

        if (_timer != null) return;

        _timer = this.RegisterGrainTimer(
            async _ => await ExecuteBotCycle(),
            new GrainTimerCreationOptions
            {
                Period = TimeSpan.FromMinutes(1),
                DueTime = TimeSpan.FromMinutes(1),
                KeepAlive = true
            });
    }

    public async Task StopAsync()
    {
        // The check is now against the registry status
        var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
        var botStatus = await botRegistry.GetBotStatus(this.GetPrimaryKey());
        if (botStatus == BotStatus.Stopped)
        {
            _logger.LogInformation("Bot {GrainId} is already stopped", this.GetPrimaryKey());
            return;
        }

        try
        {
            StopAndDisposeTimer();
            await UnregisterReminder();

            // Sync state from the volatile TradingBotBase before destroying it
            SyncStateFromBase();
            await _state.WriteStateAsync();
            await SaveBotAsync(BotStatus.Stopped);
            _tradingBot = null;

            await UpdateBotRegistryStatus(BotStatus.Stopped);
            _logger.LogInformation("LiveTradingBotGrain {GrainId} stopped successfully", this.GetPrimaryKey());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to stop LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
            throw;
        }
    }

    private void StopAndDisposeTimer()
    {
        if (_timer != null)
        {
            // Stop the timer
            _timer?.Dispose();
            _timer = null;
        }
    }

    private async Task UnregisterReminder()
    {
        var reminder = await this.GetReminder(_reminderName);
        if (reminder != null)
        {
            await this.UnregisterReminder(reminder);
        }
    }

    /// <summary>
    /// Creates a TradingBotBase instance using composition
    /// </summary>
    private TradingBotBase CreateTradingBotInstance(TradingBotConfig config)
    {
        if (string.IsNullOrEmpty(config.AccountName))
        {
            throw new InvalidOperationException("Account name is required for live trading");
        }

        using var scope = _scopeFactory.CreateScope();
        var logger = scope.ServiceProvider.GetRequiredService<ILogger<TradingBotBase>>();
        var tradingBot = new TradingBotBase(logger, _scopeFactory, config);

        // Load state into the trading bot instance
        LoadStateIntoTradingBot(tradingBot);

        return tradingBot;
    }

    /// <summary>
    /// Loads grain state into a trading bot instance
    /// </summary>
    private void LoadStateIntoTradingBot(TradingBotBase tradingBot)
    {
        tradingBot.Signals = _state.State.Signals;
        tradingBot.Positions = _state.State.Positions;
        tradingBot.WalletBalances = _state.State.WalletBalances;
        tradingBot.PreloadedCandlesCount = _state.State.PreloadedCandlesCount;
        tradingBot.ExecutionCount = _state.State.ExecutionCount;
        tradingBot.Identifier = _state.State.Identifier;
        tradingBot.LastPositionClosingTime = _state.State.LastPositionClosingTime;
        tradingBot.LastCandle = _state.State.LastCandle;
    }


    /// <summary>
    /// Executes one cycle of the trading bot
    /// </summary>
    private async Task ExecuteBotCycle()
    {
        try
        {
            if (_tradingBot == null)
            {
                return;
            }

            // Check broker balance before running
            var balances = await ServiceScopeHelpers.WithScopedService<IExchangeService, List<Balance>>(_scopeFactory, async exchangeService =>
            {
                return await exchangeService.GetBalances(_tradingBot.Account, false);
                
            });

            var usdcBalance = balances.FirstOrDefault(b => b.TokenName == Ticker.USDC.ToString());
            var ethBalance = balances.FirstOrDefault(b => b.TokenName == Ticker.ETH.ToString());
            
            // Check USDC balance first
            if (usdcBalance?.Value < Constants.GMX.Config.MinimumPositionAmount)
            {
                await _tradingBot.LogWarning(
                    $"USDC balance is below {Constants.GMX.Config.MinimumPositionAmount} USD (actual: {usdcBalance?.Value:F2}). Stopping bot {_tradingBot.Identifier}.");
                
                await StopAsync();
                return;
            }

            // Check ETH balance and perform automatic swap if needed
            var ethValueInUsd = ethBalance?.Value * ethBalance?.Price ?? 0;
            if (ethValueInUsd < 2) // ETH balance below 2 USD
            {
                await _tradingBot.LogWarning(
                    $"ETH balance is below 2 USD (actual: {ethValueInUsd:F2}). Attempting to swap USDC to ETH.");
                
                // Check if we have enough USDC for the swap
                if (usdcBalance?.Value >= 5) // Need at least 5 USD for swap
                {
                    try
                    {
                        var swapInfo = await ServiceScopeHelpers.WithScopedService<IAccountService, SwapInfos>(_scopeFactory, async accountService =>
                        {
                            return await accountService.SwapGmxTokensAsync(_state.State.User, _tradingBot.Account.Name, Ticker.USDC, Ticker.ETH, 5);
                        });

                        if (swapInfo.Success)
                        {
                            await NotifyUserAboutSwap(true, 5, swapInfo.Hash);
                        }
                        else
                        {
                            await NotifyUserAboutSwap(false, 5, null, swapInfo.Error ?? swapInfo.Message);
                            await StopAsync();
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        await NotifyUserAboutSwap(false, 5, null, ex.Message);
                    }
                }
                else
                {
                    // Both USDC and ETH are low - stop the strategy
                    await _tradingBot.LogWarning(
                        $"Both USDC ({usdcBalance?.Value:F2}) and ETH ({ethValueInUsd:F2}) balances are low. Stopping bot {_tradingBot.Identifier}.");
                    
                    await StopAsync();
                    return;
                }
            }

            // Execute the bot's Run method
            await _tradingBot.Run();
            SyncStateFromBase();
            await _state.WriteStateAsync();

            // Save bot statistics to database
            await SaveBotAsync(BotStatus.Running);
        }
        catch (ObjectDisposedException)
        {
            // Gracefully handle disposed service provider during shutdown
            _logger.LogInformation("Service provider disposed during shutdown for LiveTradingBotGrain {GrainId}",
                this.GetPrimaryKey());
            return;
        }
        catch (Exception ex)
        {
            // TODO : Turn off the bot if an error occurs
            _logger.LogError(ex, "Error during bot execution cycle for LiveTradingBotGrain {GrainId}",
                this.GetPrimaryKey());
        }
    }


    public async Task<Position> OpenPositionManuallyAsync(TradeDirection direction)
    {
        try
        {
            if (_tradingBot == null)
            {
                throw new InvalidOperationException("Bot is not running");
            }

            // Ensure LastCandle is available for manual position opening
            if (_tradingBot.LastCandle == null)
            {
                _logger.LogInformation("LastCandle is null, loading latest candle data for manual position opening");
                await _tradingBot.LoadLastCandle();
                
                // Sync the loaded candle to grain state
                SyncStateFromBase();
                await _state.WriteStateAsync();
            }

            return await _tradingBot.OpenPositionManually(direction);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to open manual position for LiveTradingBotGrain {GrainId}",
                this.GetPrimaryKey());
            throw;
        }
    }


    public Task<LiveTradingBotModel> GetBotDataAsync()
    {
        try
        {
            if (_tradingBot == null)
            {
                // For non-running bots, return data from grain state only
                return Task.FromResult(new LiveTradingBotModel
                {
                    Identifier = _state.State.Identifier,
                    Name = _state.State.Config?.Name ?? "Unknown",
                    Config = _state.State.Config,
                    Positions = _state.State.Positions ?? new Dictionary<Guid, Position>(),
                    Signals = _state.State.Signals,
                    WalletBalances = _state.State.WalletBalances ?? new Dictionary<DateTime, decimal>(),
                    ProfitAndLoss = 0, // Calculate from persisted positions if needed
                    WinRate = 0, // Calculate from persisted positions if needed
                    ExecutionCount = _state.State.ExecutionCount,
                    StartupTime = _state.State.StartupTime,
                    CreateDate = _state.State.CreateDate
                });
            }

            // For running bots, return live data
            return Task.FromResult(new LiveTradingBotModel
            {
                Identifier = _state.State.Identifier,
                Name = _state.State.Config?.Name ?? "Unknown",
                Config = _state.State.Config,
                Positions = _tradingBot.Positions,
                Signals = _tradingBot.Signals,
                WalletBalances = _tradingBot.WalletBalances,
                ProfitAndLoss = _tradingBot.GetProfitAndLoss(),
                WinRate = _tradingBot.GetWinRate(),
                ExecutionCount = _state.State.ExecutionCount,
                StartupTime = _state.State.StartupTime,
                CreateDate = _state.State.CreateDate
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to get bot data for LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
            throw;
        }
    }


    private void SyncStateFromBase()
    {
        if (_tradingBot == null) return;
        _state.State.Signals = _tradingBot.Signals;
        _state.State.Positions = _tradingBot.Positions;
        _state.State.WalletBalances = _tradingBot.WalletBalances;
        _state.State.PreloadedCandlesCount = _tradingBot.PreloadedCandlesCount;
        _state.State.ExecutionCount = _tradingBot.ExecutionCount;
        _state.State.Identifier = _tradingBot.Identifier;
        _state.State.LastPositionClosingTime = _tradingBot.LastPositionClosingTime;
        _state.State.LastCandle = _tradingBot.LastCandle;
        _state.State.Config = _tradingBot.Config;
    }

    public async Task<bool> UpdateConfiguration(TradingBotConfig newConfig)
    {
        if (_tradingBot == null)
        {
            // For non-running bots, just update the configuration
            _state.State.Config = newConfig;
            await _state.WriteStateAsync();
            
            var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
            var status = await botRegistry.GetBotStatus(this.GetPrimaryKey());
            await SaveBotAsync(status);
            return true;
        }

        var result = await _tradingBot.UpdateConfiguration(newConfig);

        if (result)
        {
            var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
            var status = await botRegistry.GetBotStatus(this.GetPrimaryKey());
            _state.State.Config = newConfig;
            await _state.WriteStateAsync();
            await SaveBotAsync(status);
        }

        return result;
    }

    public Task<Account> GetAccount()
    {
        if (_tradingBot == null)
        {
            throw new InvalidOperationException("Bot is not running - cannot get account information");
        }
        return Task.FromResult(_tradingBot.Account);
    }

    public Task<TradingBotConfig> GetConfiguration()
    {
        return Task.FromResult(_state.State.Config);
    }

    public async Task<Position> ClosePositionAsync(Guid positionId)
    {
        if (_tradingBot == null)
        {
            throw new InvalidOperationException("Bot is not running");
        }

        if (!_tradingBot.Positions.TryGetValue(positionId, out var position))
        {
            throw new InvalidOperationException($"Position with ID {positionId} not found");
        }

        var signal = _tradingBot.Signals.TryGetValue(position.SignalIdentifier, out var foundSignal)
            ? foundSignal
            : null;
        if (signal == null)
        {
            throw new InvalidOperationException($"Signal with ID {position.SignalIdentifier} not found");
        }

        await _tradingBot.CloseTrade(signal, position, position.Open, _tradingBot.LastCandle.Close, true);

        return position;
    }

    public async Task RestartAsync()
    {
        _logger.LogInformation("Restarting LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
        
        try
        {
            await StopAsync();
            
            // Add a small delay to ensure stop operations complete
            await Task.Delay(100);
            
            await StartAsync();
            
            // Verify the restart was successful
            var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
            var finalStatus = await botRegistry.GetBotStatus(this.GetPrimaryKey());
            
            _logger.LogInformation("LiveTradingBotGrain {GrainId} restart completed with final status: {Status}", 
                this.GetPrimaryKey(), finalStatus);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to restart LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
            throw;
        }
    }

    public async Task DeleteAsync()
    {
        try
        {
            // Stop the bot first if it's running
            await StopAsync();

            // Unregister from the bot registry
            var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
            await botRegistry.UnregisterBot(_state.State.Identifier);

            // Unregister from the user's agent
            if (_state.State.User != null)
            {
                var agentGrain = GrainFactory.GetGrain<IAgentGrain>(_state.State.User.Id);
                await agentGrain.UnregisterBotAsync(_state.State.Identifier);
            }

            // Clear the state
            _tradingBot = null;
            await _state.ClearStateAsync();

            _logger.LogInformation("LiveTradingBotGrain {GrainId} deleted successfully", this.GetPrimaryKey());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to delete LiveTradingBotGrain {GrainId}", this.GetPrimaryKey());
            throw;
        }
    }

    /// <summary>
    /// Updates the bot status in the central BotRegistry with retry logic
    /// </summary>
    private async Task UpdateBotRegistryStatus(BotStatus status)
    {
        const int maxRetries = 3;
        var botId = this.GetPrimaryKey();
        
        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            try
            {
                var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);
                await botRegistry.UpdateBotStatus(botId, status);
                
                // Verify the update was successful
                var actualStatus = await botRegistry.GetBotStatus(botId);
                if (actualStatus == status)
                {
                    _logger.LogDebug("Bot {BotId} status successfully updated to {Status} in BotRegistry (attempt {Attempt})", 
                        botId, status, attempt);
                    return;
                }
                else
                {
                    _logger.LogWarning("Bot {BotId} status update verification failed. Expected: {Expected}, Actual: {Actual} (attempt {Attempt})", 
                        botId, status, actualStatus, attempt);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to update bot {BotId} status to {Status} in BotRegistry (attempt {Attempt})", 
                    botId, status, attempt);
                
                if (attempt == maxRetries)
                {
                    throw;
                }
            }
            
            // Wait before retry
            if (attempt < maxRetries)
            {
                await Task.Delay(TimeSpan.FromMilliseconds(100 * attempt));
            }
        }
    }

    public async Task ReceiveReminder(string reminderName, TickStatus status)
    {
        _logger.LogInformation("Reminder '{ReminderName}' received for grain {GrainId}.", reminderName,
            this.GetPrimaryKey());

        if (reminderName == _reminderName)
        {
            // Now a single, clean call to the method that handles all the logic
            await ResumeBotIfRequiredAsync();
        }
    }

    /// <summary>
    /// Saves the current bot statistics to the database using BotService
    /// </summary>
    private async Task SaveBotAsync(BotStatus status)
    {
        try
        {
            Bot bot = null;
            if (_tradingBot == null || _state.State.User == null)
            {
                // Save bot statistics for saved bots
                bot = new Bot
                {
                    Identifier = _state.State.Identifier,
                    Name = _state.State.Config.Name,
                    Ticker = _state.State.Config.Ticker,
                    User = _state.State.User,
                    Status = status,
                    CreateDate = _state.State.CreateDate,
                    StartupTime = _state.State.StartupTime,
                    TradeWins = 0,
                    TradeLosses = 0,
                    Pnl = 0,
                    Roi = 0,
                    Volume = 0,
                    Fees = 0
                };
            }
            else
            {
                // Calculate statistics using TradingBox helpers
                var (tradeWins, tradeLosses) = TradingBox.GetWinLossCount(_tradingBot.Positions);
                var pnl = _tradingBot.GetProfitAndLoss();
                var fees = _tradingBot.GetTotalFees();
                var volume = TradingBox.GetTotalVolumeTraded(_tradingBot.Positions);

                // Calculate ROI based on total investment
                var totalInvestment = _tradingBot.Positions.Values
                    .Sum(p => p.Open.Quantity * p.Open.Price);
                var roi = totalInvestment > 0 ? (pnl / totalInvestment) * 100 : 0;

                // Calculate long and short position counts
                var longPositionCount = _tradingBot.Positions.Values
                    .Count(p => p.OriginDirection == TradeDirection.Long);
                var shortPositionCount = _tradingBot.Positions.Values
                    .Count(p => p.OriginDirection == TradeDirection.Short);

                // Create complete Bot object with all statistics
                bot = new Bot
                {
                    Identifier = _state.State.Identifier,
                    Name = _state.State.Config.Name,
                    Ticker = _state.State.Config.Ticker,
                    User = _state.State.User,
                    Status = status,
                    StartupTime = _state.State.StartupTime,
                    CreateDate = _state.State.CreateDate,
                    TradeWins = tradeWins,
                    TradeLosses = tradeLosses,
                    Pnl = pnl,
                    Roi = roi,
                    Volume = volume,
                    Fees = fees,
                    LongPositionCount = longPositionCount,
                    ShortPositionCount = shortPositionCount
                };
            }

            // Pass the complete Bot object to BotService for saving
            var success = await ServiceScopeHelpers.WithScopedService<IBotService, bool>(_scopeFactory,
                async (botService) => { return await botService.SaveBotStatisticsAsync(bot); });

            if (success)
            {
                _logger.LogDebug(
                    "Successfully saved bot statistics for bot {BotId}: Wins={Wins}, Losses={Losses}, PnL={PnL}, ROI={ROI}%, Volume={Volume}, Fees={Fees}, Long={LongPositions}, Short={ShortPositions}",
                    _state.State.Identifier, bot.TradeWins, bot.TradeLosses, bot.Pnl, bot.Roi, bot.Volume, bot.Fees, bot.LongPositionCount, bot.ShortPositionCount);
            }
            else
            {
                _logger.LogWarning("Failed to save bot statistics for bot {BotId}", _state.State.Identifier);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to save bot statistics for bot {BotId}", _state.State.Identifier);
        }
    }

    /// <summary>
    /// Notifies the user about swap operations via webhook/telegram
    /// </summary>
    private async Task NotifyUserAboutSwap(bool isSuccess, decimal amount, string? transactionHash, string? errorMessage = null)
    {
        try
        {
            var message = isSuccess
                ? $"🔄 **Automatic Swap Successful**\n\n" +
                  $"🎯 **Bot:** {_tradingBot?.Identifier}\n" +
                  $"💰 **Amount:** {amount} USDC → ETH\n" +
                  $"✅ **Status:** Success\n" +
                  $"🔗 **Transaction:** {transactionHash}\n" +
                  $"⏰ **Time:** {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"
                : $"❌ **Automatic Swap Failed**\n\n" +
                  $"🎯 **Bot:** {_tradingBot?.Identifier}\n" +
                  $"💰 **Amount:** {amount} USDC → ETH\n" +
                  $"❌ **Status:** Failed\n" +
                  $"⚠️ **Error:** {errorMessage}\n" +
                  $"⏰ **Time:** {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC";

            // Send notification via webhook service
            await ServiceScopeHelpers.WithScopedService<IWebhookService>(_scopeFactory, async webhookService =>
            {
                await webhookService.SendMessage(message, _state.State.User?.TelegramChannel);
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send swap notification for bot {BotId}", _tradingBot?.Identifier);
        }
    }
}