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

namespace Managing.Application.ManageBot
{
    public class BotService : IBotService
    {
        private readonly IBotRepository _botRepository;
        private readonly IMessengerService _messengerService;
        private readonly ILogger<TradingBotBase> _tradingBotLogger;
        private readonly ITradingService _tradingService;
        private readonly IGrainFactory _grainFactory;
        private readonly IServiceScopeFactory _scopeFactory;


        public BotService(IBotRepository botRepository,
            IMessengerService messengerService, ILogger<TradingBotBase> tradingBotLogger,
            ITradingService tradingService, IGrainFactory grainFactory, IServiceScopeFactory scopeFactory)
        {
            _botRepository = botRepository;
            _messengerService = messengerService;
            _tradingBotLogger = tradingBotLogger;
            _tradingService = tradingService;
            _grainFactory = grainFactory;
            _scopeFactory = scopeFactory;
        }

        public async Task<IEnumerable<Bot>> GetBotsAsync()
        {
            return await _botRepository.GetBotsAsync();
        }

        public async Task<IEnumerable<Bot>> GetBotsByStatusAsync(BotStatus status)
        {
            return await _botRepository.GetBotsByStatusAsync(status);
        }

        public async Task<BotStatus> StopBot(Guid identifier)
        {
            try
            {
                var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);
                await grain.StopAsync();
                return BotStatus.Stopped;
            }
            catch (Exception e)
            {
                _tradingBotLogger.LogError(e, "Error stopping bot {Identifier}", identifier);
                return BotStatus.Stopped;
            }
        }

        public async Task<bool> DeleteBot(Guid identifier)
        {
            var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);

            try
            {
                var config = await grain.GetConfiguration();
                var account = await grain.GetAccount();
                await grain.StopAsync();
                await _botRepository.DeleteBot(identifier);
                await grain.DeleteAsync();

                var deleteMessage = $"🗑️ **Bot Deleted**\n\n" +
                                    $"🎯 **Agent:** {account.User.AgentName}\n" +
                                    $"🤖 **Bot Name:** {config.Name}\n" +
                                    $"⏰ **Deleted At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
                                    $"⚠️ **Bot has been permanently deleted and all data removed.**";

                await _messengerService.SendTradeMessage(deleteMessage, false, account.User);
                return true;
            }
            catch (Exception e)
            {
                _tradingBotLogger.LogError(e, "Error deleting bot {Identifier}", identifier);
                return false;
            }
        }

        public async Task<BotStatus> RestartBot(Guid identifier)
        {
            try
            {
                var registryGrain = _grainFactory.GetGrain<ILiveBotRegistryGrain>(0);
                var previousStatus = await registryGrain.GetBotStatus(identifier);

                // If bot is already up, return the status directly
                if (previousStatus == BotStatus.Running)
                {
                    return BotStatus.Running;
                }

                var botGrain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);
                if (previousStatus == BotStatus.Saved)
                {
                    // First time startup
                    await botGrain.StartAsync();
                    var grainState = await botGrain.GetBotDataAsync();
                    var account = await botGrain.GetAccount();
                    var startupMessage = $"🚀 **Bot Started**\n\n" +
                                         $"🎯 **Agent:** {account.User.AgentName}\n" +
                                         $"🤖 **Bot Name:** {grainState.Config.Name}\n" +
                                         $"⏰ **Started At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n" +
                                         $"🕐 **Startup Time:** {grainState.StartupTime:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
                                         $"✅ **Bot has been successfully started and is now active.**";

                    await _messengerService.SendTradeMessage(startupMessage, false, account.User);
                }
                else
                {
                    // Restart (bot was previously down)
                    await botGrain.RestartAsync();
                    var grainState = await botGrain.GetBotDataAsync();
                    var account = await botGrain.GetAccount();
                    var restartMessage = $"🔄 **Bot Restarted**\n\n" +
                                         $"🎯 **Agent:** {account.User.AgentName}\n" +
                                         $"🤖 **Bot Name:** {grainState.Config.Name}\n" +
                                         $"⏰ **Restarted At:** {DateTime.UtcNow:MMM dd, yyyy • HH:mm:ss} UTC\n" +
                                         $"🕐 **New Startup Time:** {grainState.StartupTime:MMM dd, yyyy • HH:mm:ss} UTC\n\n" +
                                         $"🚀 **Bot has been successfully restarted and is now active.**";

                    await _messengerService.SendTradeMessage(restartMessage, false, account.User);
                }

                return BotStatus.Running;
            }
            catch (Exception e)
            {
                _tradingBotLogger.LogError(e, "Error restarting bot {Identifier}", identifier);
                return BotStatus.Stopped;
            }
        }

        private async Task<Bot> GetBot(Guid identifier)
        {
            var bot = await _botRepository.GetBotByIdentifierAsync(identifier);
            return bot;
        }

        /// <summary>
        /// Updates the configuration of an existing bot without stopping and restarting it.
        /// </summary>
        /// <param name="identifier">The bot identifier</param>
        /// <param name="newConfig">The new configuration to apply</param>
        /// <returns>True if the configuration was successfully updated, false otherwise</returns>
        public async Task<bool> UpdateBotConfiguration(Guid identifier, TradingBotConfig newConfig)
        {
            var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);
            // Ensure the scenario is properly loaded from database if needed
            if (newConfig.Scenario == null && !string.IsNullOrEmpty(newConfig.ScenarioName))
            {
                var scenario = await _tradingService.GetScenarioByNameAsync(newConfig.ScenarioName);
                if (scenario != null)
                {
                    newConfig.Scenario = LightScenario.FromScenario(scenario);
                }
                else
                {
                    throw new ArgumentException(
                        $"Scenario '{newConfig.ScenarioName}' not found in database when updating configuration");
                }
            }

            if (newConfig.Scenario == null)
            {
                throw new ArgumentException(
                    "Scenario object must be provided or ScenarioName must be valid when updating configuration");
            }

            return await grain.UpdateConfiguration(newConfig);
        }

        public async Task<TradingBotConfig> GetBotConfig(Guid identifier)
        {
            var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);
            return await grain.GetConfiguration();
        }

        public async Task<IEnumerable<string>> GetActiveBotsNamesAsync()
        {
            var bots = await _botRepository.GetBotsByStatusAsync(BotStatus.Running);
            return bots.Select(b => b.Name);
        }

        public async Task<IEnumerable<Bot>> GetBotsByUser(int id)
        {
            return await _botRepository.GetBotsByUserIdAsync(id);
        }

        public async Task<IEnumerable<Bot>> GetBotsByIdsAsync(IEnumerable<Guid> botIds)
        {
            return await _botRepository.GetBotsByIdsAsync(botIds);
        }

        public async Task<Bot> GetBotByName(string name)
        {
            return await _botRepository.GetBotByNameAsync(name);
        }

        public async Task<Bot> GetBotByIdentifier(Guid identifier)
        {
            return await _botRepository.GetBotByIdentifierAsync(identifier);
        }

        public async Task<Position> OpenPositionManuallyAsync(Guid identifier, TradeDirection direction)
        {
            var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);
            return await grain.OpenPositionManuallyAsync(direction);
        }

        public async Task<Position> ClosePositionAsync(Guid identifier, Guid positionId)
        {
            var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);
            return await grain.ClosePositionAsync(positionId);
        }

        public async Task<bool> UpdateBotStatisticsAsync(Guid identifier)
        {
            try
            {
                var grain = _grainFactory.GetGrain<ILiveTradingBotGrain>(identifier);
                var botData = await grain.GetBotDataAsync();

                // Get the current bot from database
                var existingBot = await _botRepository.GetBotByIdentifierAsync(identifier);
                if (existingBot == null)
                {
                    _tradingBotLogger.LogWarning("Bot {Identifier} not found in database for statistics update",
                        identifier);
                    return false;
                }

                // Calculate statistics using TradingBox helpers
                var (tradeWins, tradeLosses) = TradingBox.GetWinLossCount(botData.Positions);
                var pnl = botData.ProfitAndLoss;
                var fees = botData.Positions.Values.Sum(p =>
                {
                    if (p.Open.Price > 0 && p.Open.Quantity > 0)
                    {
                        var positionSizeUsd = (p.Open.Price * p.Open.Quantity) * p.Open.Leverage;
                        var uiFeeRate = 0.001m; // 0.1%
                        var uiFeeOpen = positionSizeUsd * uiFeeRate;
                        var networkFeeForOpening = 0.50m;
                        return uiFeeOpen + networkFeeForOpening;
                    }

                    return 0;
                });
                var volume = TradingBox.GetTotalVolumeTraded(botData.Positions);

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

                // Update bot statistics
                existingBot.TradeWins = tradeWins;
                existingBot.TradeLosses = tradeLosses;
                existingBot.Pnl = pnl;
                existingBot.Roi = roi;
                existingBot.Volume = volume;
                existingBot.Fees = fees;

                // Use the new SaveBotStatisticsAsync method
                return await SaveBotStatisticsAsync(existingBot);
            }
            catch (Exception e)
            {
                _tradingBotLogger.LogError(e, "Error updating bot statistics for {Identifier}", identifier);
                return false;
            }
        }

        public async Task<bool> SaveBotStatisticsAsync(Bot bot)
        {
            try
            {
                if (bot == null)
                {
                    _tradingBotLogger.LogWarning("Cannot save bot statistics: bot object is null");
                    return false;
                }

                var existingBot = await _botRepository.GetBotByIdentifierAsync(bot.Identifier);

                // Check if bot already exists in database
                await ServiceScopeHelpers.WithScopedService<IBotRepository>(
                    _scopeFactory,
                    async repo =>
                    {
                        if (existingBot == null)
                        {
                            _tradingBotLogger.LogInformation("Updating existing bot statistics for bot {BotId}",
                                bot.Identifier);
                            // Insert new bot
                            await repo.InsertBotAsync(bot);
                            _tradingBotLogger.LogInformation(
                                "Created new bot statistics for bot {BotId}: Wins={Wins}, Losses={Losses}, PnL={PnL}, ROI={ROI}%, Volume={Volume}, Fees={Fees}",
                                bot.Identifier, bot.TradeWins, bot.TradeLosses, bot.Pnl, bot.Roi, bot.Volume, bot.Fees);
                        }
                        else
                        {
                            _tradingBotLogger.LogInformation("Creating new bot statistics for bot {BotId}",
                                bot.Identifier);
                            // Update existing bot
                            await repo.UpdateBot(bot);
                            _tradingBotLogger.LogDebug(
                                "Updated bot statistics for bot {BotId}: Wins={Wins}, Losses={Losses}, PnL={PnL}, ROI={ROI}%, Volume={Volume}, Fees={Fees}",
                                bot.Identifier, bot.TradeWins, bot.TradeLosses, bot.Pnl, bot.Roi, bot.Volume, bot.Fees);
                        }
                    });

                return true;
            }
            catch (Exception e)
            {
                _tradingBotLogger.LogError(e, "Error saving bot statistics for bot {BotId}", bot?.Identifier);
                return false;
            }
        }

        public async Task<(IEnumerable<Bot> Bots, int TotalCount)> GetBotsPaginatedAsync(
            int pageNumber,
            int pageSize,
            BotStatus? status = null,
            string? name = null,
            string? ticker = null,
            string? agentName = null,
            string sortBy = "CreateDate",
            string sortDirection = "Desc")
        {
            return await ServiceScopeHelpers.WithScopedService<IBotRepository, (IEnumerable<Bot> Bots, int TotalCount)>(
                _scopeFactory,
                async repo =>
                {
                    return await repo.GetBotsPaginatedAsync(
                        pageNumber,
                        pageSize,
                        status,
                        name,
                        ticker,
                        agentName,
                        sortBy,
                        sortDirection);
                });
        }
    }
}