using Managing.Api.Extensions;
using Managing.Api.Models.Requests;
using Managing.Api.Models.Responses;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Application.ManageBot.Commands;
using Managing.Domain.Backtests;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.Scenarios;
using Managing.Domain.Statistics;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Trades;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;

namespace Managing.Api.Controllers;

/// <summary>
/// Controller for handling data-related operations such as retrieving tickers, spotlight data, and candles.
/// Requires authorization for access.
/// </summary>
[AllowAnonymous]
[ApiController]
[Route("[controller]")]
public class DataController : ControllerBase
{
    private readonly IExchangeService _exchangeService;
    private readonly IAccountService _accountService;
    private readonly ICacheService _cacheService;
    private readonly IStatisticService _statisticService;
    private readonly IAgentService _agentService;
    private readonly IHubContext<CandleHub> _hubContext;
    private readonly IMediator _mediator;
    private readonly ITradingService _tradingService;
    private readonly IGrainFactory _grainFactory;

    /// <summary>
    /// Initializes a new instance of the <see cref="DataController"/> class.
    /// </summary>
    /// <param name="exchangeService">Service for interacting with exchanges.</param>
    /// <param name="accountService">Service for account management.</param>
    /// <param name="cacheService">Service for caching data.</param>
    /// <param name="statisticService">Service for statistical analysis.</param>
    /// <param name="agentService">Service for agents</param>
    /// <param name="hubContext">SignalR hub context for real-time communication.</param>
    /// <param name="mediator">Mediator for handling commands and queries.</param>
    /// <param name="tradingService">Service for trading operations.</param>
    /// <param name="grainFactory">Orleans grain factory for accessing grains.</param>
    public DataController(
        IExchangeService exchangeService,
        IAccountService accountService,
        ICacheService cacheService,
        IStatisticService statisticService,
        IAgentService agentService,
        IHubContext<CandleHub> hubContext,
        IMediator mediator,
        ITradingService tradingService,
        IGrainFactory grainFactory)
    {
        _exchangeService = exchangeService;
        _accountService = accountService;
        _cacheService = cacheService;
        _statisticService = statisticService;
        _agentService = agentService;
        _hubContext = hubContext;
        _mediator = mediator;
        _tradingService = tradingService;
        _grainFactory = grainFactory;
    }

    /// <summary>
    /// Retrieves tickers for a given account and timeframe, utilizing caching to improve performance.
    /// </summary>
    /// <param name="timeframe">The timeframe for which to retrieve tickers.</param>
    /// <returns>An array of tickers.</returns>
    [HttpPost("GetTickers")]
    public async Task<ActionResult<List<TickerInfos>>> GetTickers(Timeframe timeframe)
    {
        var cacheKey = string.Concat(timeframe.ToString());
        var tickers = _cacheService.GetValue<List<TickerInfos>>(cacheKey);

        if (tickers == null || tickers.Count == 0)
        {
            var availableTicker = await _exchangeService.GetTickers(timeframe);

            tickers = MapTickerToTickerInfos(availableTicker);

            _cacheService.SaveValue(cacheKey, tickers, TimeSpan.FromHours(2));
        }

        return Ok(tickers);
    }

    private List<TickerInfos> MapTickerToTickerInfos(List<Ticker> availableTicker)
    {
        var tickerInfos = new List<TickerInfos>();
        var tokens = new Dictionary<string, string>
        {
            { "AAVE", "https://assets.coingecko.com/coins/images/12645/standard/AAVE.png?1696512452" },
            { "ADA", "https://assets.coingecko.com/coins/images/975/standard/cardano.png?1696502090" },
            { "APE", "https://assets.coingecko.com/coins/images/24383/standard/apecoin.jpg?1696523566" },
            {
                "ARB", "https://assets.coingecko.com/coins/images/16547/small/photo_2023-03-29_21.47.00.jpeg?1680097630"
            },
            { "ATOM", "https://assets.coingecko.com/coins/images/1481/standard/cosmos_hub.png?1696502525" },
            { "AVAX", "https://assets.coingecko.com/coins/images/12559/small/coin-round-red.png?1604021818" },
            { "BNB", "https://assets.coingecko.com/coins/images/825/standard/bnb-icon2_2x.png?1696501970" },
            { "BTC", "https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579" },
            { "DOGE", "https://assets.coingecko.com/coins/images/5/small/dogecoin.png?1547792256" },
            {
                "DOT",
                "https://static.coingecko.com/s/polkadot-73b0c058cae10a2f076a82dcade5cbe38601fad05d5e6211188f09eb96fa4617.gif"
            },
            { "ETH", "https://assets.coingecko.com/coins/images/279/small/ethereum.png?1595348880" },
            { "FIL", "https://assets.coingecko.com/coins/images/12817/standard/filecoin.png?1696512609" },
            { "GMX", "https://assets.coingecko.com/coins/images/18323/small/arbit.png?1631532468" },
            { "LINK", "https://assets.coingecko.com/coins/images/877/thumb/chainlink-new-logo.png?1547034700" },
            { "LTC", "https://assets.coingecko.com/coins/images/2/small/litecoin.png?1547033580" },
            { "MATIC", "https://assets.coingecko.com/coins/images/32440/standard/polygon.png?1698233684" },
            { "NEAR", "https://assets.coingecko.com/coins/images/10365/standard/near.jpg?1696510367" },
            { "OP", "https://assets.coingecko.com/coins/images/25244/standard/Optimism.png?1696524385" },
            { "PEPE", "https://assets.coingecko.com/coins/images/29850/standard/pepe-token.jpeg?1696528776" },
            { "SOL", "https://assets.coingecko.com/coins/images/4128/small/solana.png?1640133422" },
            { "UNI", "https://assets.coingecko.com/coins/images/12504/thumb/uniswap-uni.png?1600306604" },
            { "USDC", "https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389" },
            { "USDT", "https://assets.coingecko.com/coins/images/325/thumb/Tether-logo.png?1598003707" },
            { "WIF", "https://assets.coingecko.com/coins/images/33566/standard/dogwifhat.jpg?1702499428" },
            { "XRP", "https://assets.coingecko.com/coins/images/44/small/xrp-symbol-white-128.png?1605778731" },
            { "SHIB", "https://assets.coingecko.com/coins/images/11939/standard/shiba.png?1696511800" },
            { "STX", "https://assets.coingecko.com/coins/images/2069/standard/Stacks_Logo_png.png?1709979332" },
            { "ORDI", "https://assets.coingecko.com/coins/images/30162/standard/ordi.png?1696529082" },
            { "APT", "https://assets.coingecko.com/coins/images/26455/standard/aptos_round.png?1696525528" },
            { "BOME", "https://assets.coingecko.com/coins/images/36071/standard/bome.png?1710407255" },
            { "MEME", "https://assets.coingecko.com/coins/images/32528/standard/memecoin_%282%29.png?1698912168" },
            { "FLOKI", "https://assets.coingecko.com/coins/images/16746/standard/PNG_image.png?1696516318" },
            { "MEW", "https://assets.coingecko.com/coins/images/36440/standard/MEW.png?1711442286" },
            { "TAO", "https://assets.coingecko.com/coins/images/28452/standard/ARUsPeNQ_400x400.jpeg?1696527447" },
            { "BONK", "https://assets.coingecko.com/coins/images/28600/standard/bonk.jpg?1696527587" },
            { "WLD", "https://assets.coingecko.com/coins/images/31069/standard/worldcoin.jpeg?1696529903" },
            {
                "tBTC",
                "https://assets.coingecko.com/coins/images/11224/standard/0x18084fba666a33d37592fa2633fd49a74dd93a88.png?1696511155"
            },
            { "EIGEN", "https://assets.coingecko.com/coins/images/37441/standard/eigen.jpg?1728023974" },
            { "SUI", "https://assets.coingecko.com/coins/images/26375/standard/sui-ocean-square.png?1727791290" },
            { "SEI", "https://assets.coingecko.com/coins/images/28205/standard/Sei_Logo_-_Transparent.png?1696527207" },
            { "DAI", "https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734" },
            { "TIA", "https://assets.coingecko.com/coins/images/31967/standard/tia.jpg?1696530772" },
            { "TRX", "https://assets.coingecko.com/coins/images/1094/standard/tron-logo.png?1696502193" },
            {
                "TON",
                "https://assets.coingecko.com/coins/images/17980/standard/photo_2024-09-10_17.09.00.jpeg?1725963446"
            },
            {
                "PENDLE",
                "https://assets.coingecko.com/coins/images/15069/standard/Pendle_Logo_Normal-03.png?1696514728"
            },
            { "wstETH", "https://assets.coingecko.com/coins/images/18834/standard/wstETH.png?1696518295" },
            { "USDe", "https://assets.coingecko.com/coins/images/33613/standard/USDE.png?1716355685" },
            { "SATS", "https://assets.coingecko.com/coins/images/30666/standard/_dD8qr3M_400x400.png?1702913020" },
            { "POL", "https://assets.coingecko.com/coins/images/32440/standard/polygon.png?1698233684" },
            { "XLM", "https://assets.coingecko.com/coins/images/100/standard/Stellar_symbol_black_RGB.png?1696501482" },
            { "BCH", "https://assets.coingecko.com/coins/images/780/standard/bitcoin-cash-circle.png?1696501932" },
            { "ICP", "https://assets.coingecko.com/coins/images/14495/standard/Internet_Computer_logo.png?1696514180" },
            { "RENDER", "https://assets.coingecko.com/coins/images/11636/standard/rndr.png?1696511529" },
            { "INJ", "https://assets.coingecko.com/coins/images/12882/standard/Secondary_Symbol.png?1696512670" },
            { "TRUMP", "https://assets.coingecko.com/coins/images/53746/standard/trump.png?1737171561" },
            { "MELANIA", "https://assets.coingecko.com/coins/images/53775/standard/melania-meme.png?1737329885" },
            { "ENA", "https://assets.coingecko.com/coins/images/36530/standard/ethena.png?1711701436" },
            { "FARTCOIN", "https://assets.coingecko.com/coins/images/50891/standard/fart.jpg?1729503972" },
            { "AI16Z", "https://assets.coingecko.com/coins/images/51090/standard/AI16Z.jpg?1730027175" },
            { "ANIME", "https://assets.coingecko.com/coins/images/53575/standard/anime.jpg?1736748703" },
            { "BERA", "https://assets.coingecko.com/coins/images/25235/standard/BERA.png?1738822008" },
            { "VIRTUAL", "https://assets.coingecko.com/coins/images/34057/standard/LOGOMARK.png?1708356054" },
            {
                "PENGU",
                "https://assets.coingecko.com/coins/images/52622/standard/PUDGY_PENGUINS_PENGU_PFP.png?1733809110"
            },
            { "FET", "https://assets.coingecko.com/coins/images/5681/standard/ASI.png?1719827289" },
            { "ONDO", "https://assets.coingecko.com/coins/images/26580/standard/ONDO.png?1696525656" },
            { "AIXBT", "https://assets.coingecko.com/coins/images/51784/standard/3.png?1731981138" },
            {
                "CAKE",
                "https://assets.coingecko.com/coins/images/12632/standard/pancakeswap-cake-logo_%281%29.png?1696512440"
            },
            { "S", "https://assets.coingecko.com/coins/images/38108/standard/200x200_Sonic_Logo.png?1734679256" },
            { "JUP", "https://assets.coingecko.com/coins/images/34188/standard/jup.png?1704266489" },
            { "HYPE", "https://assets.coingecko.com/coins/images/50882/standard/hyperliquid.jpg?1729431300" },
            { "OM", "https://assets.coingecko.com/coins/images/12151/standard/OM_Token.png?1696511991" }
        };

        foreach (var ticker in availableTicker)
        {
            var tickerInfo = new TickerInfos
            {
                Ticker = ticker,
                ImageUrl = tokens.GetValueOrDefault(ticker.ToString(),
                    "https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579") // Default to BTC image if not found
            };
            tickerInfos.Add(tickerInfo);
        }

        return tickerInfos;
    }

    /// <summary>
    /// Retrieves the latest spotlight overview, using caching to enhance response times.
    /// </summary>
    /// <returns>A <see cref="SpotlightOverview"/> object containing spotlight data.</returns>
    [Authorize]
    [HttpGet("Spotlight")]
    public async Task<ActionResult<SpotlightOverview>> GetSpotlight()
    {
        var cacheKey = $"Spotlight_{DateTime.Now.AddDays(-2).ToString("yyyy-MM-dd")}";
        var overview = _cacheService.GetValue<SpotlightOverview>(cacheKey);
        if (overview == null)
        {
            overview = await _statisticService.GetLastSpotlight(DateTime.Now.AddDays(-2));
            _cacheService.SaveValue(cacheKey, overview, TimeSpan.FromMinutes(2));
        }

        return Ok(overview);
    }

    /// <summary>
    /// Retrieves candles with indicators values for backtest details display.
    /// </summary>
    /// <param name="exchange">The trading exchange.</param>
    /// <param name="ticker">The ticker symbol.</param>
    /// <param name="startDate">The start date for the candles.</param>
    /// <param name="endDate">The end date for the candles.</param>
    /// <param name="timeframe">The timeframe for the candles.</param>
    /// <param name="scenario">The scenario object to calculate indicators values (optional).</param>
    /// <returns>A response containing candles and indicators values.</returns>
    [Authorize]
    [HttpPost("GetCandlesWithIndicators")]
    public async Task<ActionResult<CandlesWithIndicatorsResponse>> GetCandlesWithIndicators(
        [FromBody] GetCandlesWithIndicatorsRequest request)
    {
        try
        {
            // Get candles for the specified period
            var candles = await _exchangeService.GetCandlesInflux(TradingExchanges.Evm, request.Ticker,
                request.StartDate, request.Timeframe, request.EndDate);

            if (candles == null || candles.Count == 0)
            {
                return Ok(new CandlesWithIndicatorsResponse
                {
                    Candles = new HashSet<Candle>(),
                    IndicatorsValues = new Dictionary<IndicatorType, IndicatorsResultBase>()
                });
            }

            // Calculate indicators values if scenario is provided
            Dictionary<IndicatorType, IndicatorsResultBase> indicatorsValues = null;
            if (request.Scenario != null && request.Scenario.Indicators != null &&
                request.Scenario.Indicators.Count > 0)
            {
                // Map ScenarioRequest to domain Scenario object
                var domainScenario = MapScenarioRequestToScenario(request.Scenario);
                indicatorsValues = _tradingService.CalculateIndicatorsValuesAsync(domainScenario, candles);
            }

            return Ok(new CandlesWithIndicatorsResponse
            {
                Candles = candles,
                IndicatorsValues = indicatorsValues ?? new Dictionary<IndicatorType, IndicatorsResultBase>()
            });
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Error retrieving candles with indicators: {ex.Message}");
        }
    }

    /// <summary>
    /// Retrieves statistics about currently running bots and their change in the last 24 hours.
    /// </summary>
    /// <returns>A <see cref="StrategiesStatisticsViewModel"/> containing bot statistics.</returns>
    [HttpGet("GetStrategiesStatistics")]
    public async Task<ActionResult<StrategiesStatisticsViewModel>> GetStrategiesStatistics()
    {
        const string cacheKey = "StrategiesStatistics";
        const string previousCountKey = "PreviousBotsCount";

        // Check if the statistics are already cached
        var cachedStats = _cacheService.GetValue<StrategiesStatisticsViewModel>(cacheKey);

        if (cachedStats != null)
        {
            return Ok(cachedStats);
        }

        // Get active bots
        var activeBots = await _mediator.Send(new GetBotsByStatusCommand(BotStatus.Running));
        var currentCount = activeBots.Count();

        // Get previous count from cache
        var previousCount = _cacheService.GetValue<int>(previousCountKey);

        // Calculate change - if no previous value, set current count as the change (all are new)
        int change;
        if (previousCount == 0)
        {
            // First time running - assume all bots are new (positive change)
            change = currentCount;
        }
        else
        {
            // Calculate actual difference between current and previous count
            change = currentCount - previousCount;
        }

        // Create the response
        var botsStatistics = new StrategiesStatisticsViewModel
        {
            TotalStrategiesRunning = currentCount,
            ChangeInLast24Hours = change
        };

        // Store current count for future comparison (with 24 hour expiration)
        _cacheService.SaveValue(previousCountKey, currentCount, TimeSpan.FromHours(24));

        // Cache the statistics for 5 minutes
        _cacheService.SaveValue(cacheKey, botsStatistics, TimeSpan.FromMinutes(5));

        return Ok(botsStatistics);
    }

    /// <summary>
    /// Retrieves the top 3 performing strategies based on ROI.
    /// </summary>
    /// <returns>A <see cref="TopStrategiesViewModel"/> containing the top performing strategies.</returns>
    [HttpGet("GetTopStrategies")]
    public async Task<ActionResult<TopStrategiesViewModel>> GetTopStrategies()
    {
        // Get active bots
        var activeBots = await _mediator.Send(new GetBotsByStatusCommand(BotStatus.Running));

        // Calculate PnL for each bot once and store in a list of tuples
        var botsWithPnL = activeBots
            .Select(bot => new { Bot = bot, PnL = bot.Pnl, agentName = bot.User.AgentName })
            .OrderByDescending(item => item.PnL)
            .Take(3)
            .ToList();

        // Map to view model
        var topStrategies = new TopStrategiesViewModel
        {
            TopStrategies = botsWithPnL
                .Select(item => new StrategyPerformance
                {
                    StrategyName = item.Bot.Name,
                    PnL = item.PnL,
                    AgentName = item.agentName,
                })
                .ToList()
        };

        return Ok(topStrategies);
    }

    /// <summary>
    /// Retrieves the top 3 performing strategies based on ROI percentage.
    /// </summary>
    /// <returns>A <see cref="TopStrategiesByRoiViewModel"/> containing the top performing strategies by ROI.</returns>
    [HttpGet("GetTopStrategiesByRoi")]
    public async Task<ActionResult<TopStrategiesByRoiViewModel>> GetTopStrategiesByRoi()
    {
        // Get active bots
        var activeBots = await _mediator.Send(new GetBotsByStatusCommand(BotStatus.Running));

        // Filter bots with valid ROI data and order by ROI
        var botsWithRoi = activeBots
            .Select(bot => new { Bot = bot, Roi = bot.Roi, PnL = bot.Pnl, Volume = bot.Volume })
            .OrderByDescending(item => item.Roi)
            .Take(3)
            .ToList();

        // Map to view model
        var topStrategiesByRoi = new TopStrategiesByRoiViewModel
        {
            TopStrategiesByRoi = botsWithRoi
                .Select(item => new StrategyRoiPerformance
                {
                    StrategyName = item.Bot.Name,
                    Roi = item.Roi,
                    PnL = item.PnL,
                    Volume = item.Volume
                })
                .ToList()
        };

        return Ok(topStrategiesByRoi);
    }

    /// <summary>
    /// Retrieves the top 3 performing agents based on PnL.
    /// </summary>
    /// <returns>A <see cref="TopAgentsByPnLViewModel"/> containing the top performing agents by PnL.</returns>
    [HttpGet("GetTopAgentsByPnL")]
    public async Task<ActionResult<TopAgentsByPnLViewModel>> GetTopAgentsByPnL()
    {
        try
        {
            // Get all agent summaries
            var allAgentSummaries = await _mediator.Send(new GetAllAgentSummariesCommand("Total"));

            // Filter agents with valid PnL data and order by PnL
            var agentsWithPnL = allAgentSummaries
                .Where(agent => agent.TotalPnL != 0) // Only include agents with actual PnL
                .OrderByDescending(agent => agent.TotalPnL)
                .Take(3)
                .ToList();

            // Map to view model
            var topAgentsByPnL = new TopAgentsByPnLViewModel
            {
                TopAgentsByPnL = agentsWithPnL
                    .Select(agent => new AgentPerformance
                    {
                        AgentName = agent.AgentName,
                        PnL = agent.TotalPnL,
                        TotalROI = agent.TotalROI,
                        TotalVolume = agent.TotalVolume,
                        ActiveStrategiesCount = agent.ActiveStrategiesCount,
                        TotalBalance = agent.TotalBalance
                    })
                    .ToList()
            };

            return Ok(topAgentsByPnL);
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Error retrieving top agents by PnL: {ex.Message}");
        }
    }

    /// <summary>
    /// Retrieves list of the active strategies for a user with detailed information
    /// </summary>
    /// <param name="agentName">The agentName to retrieve strategies for</param>
    /// <returns>A list of detailed strategy information</returns>
    [HttpGet("GetUserStrategies")]
    public async Task<ActionResult<List<UserStrategyDetailsViewModel>>> GetUserStrategies(string agentName)
    {
        if (string.IsNullOrEmpty(agentName))
        {
            return BadRequest("Agent name cannot be null or empty.");
        }

        // Get all strategies for the specified user
        var userStrategies = await _mediator.Send(new GetUserStrategiesCommand(agentName));

        // Get all positions for all strategies in a single database call to avoid DbContext concurrency issues
        var strategyIdentifiers = userStrategies.Select(s => s.Identifier).ToList();
        var allPositions = await _tradingService.GetPositionsByInitiatorIdentifiersAsync(strategyIdentifiers);
        var positionsByIdentifier = allPositions.GroupBy(p => p.InitiatorIdentifier)
            .ToDictionary(g => g.Key, g => g.ToList());

        // Convert to detailed view model with additional information
        var result = userStrategies.Select(strategy => MapStrategyToViewModel(strategy, positionsByIdentifier))
            .ToList();

        return Ok(result);
    }

    /// <summary>
    /// Retrieves a specific strategy for a user by strategy name
    /// </summary>
    /// <param name="agentName">The agent/user name to retrieve the strategy for</param>
    /// <param name="strategyName">The name of the strategy to retrieve</param>
    /// <returns>Detailed information about the requested strategy</returns>
    [HttpGet("GetUserStrategy")]
    public async Task<ActionResult<UserStrategyDetailsViewModel>> GetUserStrategy(string agentName, string strategyName)
    {
        if (string.IsNullOrEmpty(agentName))
        {
            return BadRequest("Agent name cannot be null or empty.");
        }

        if (string.IsNullOrEmpty(strategyName))
        {
            return BadRequest("Strategy name cannot be null or empty.");
        }

        // Get the specific strategy for the user
        var strategy = await _mediator.Send(new GetUserStrategyCommand(agentName, strategyName));

        if (strategy == null)
        {
            return NotFound($"Strategy '{strategyName}' not found for user '{agentName}'");
        }

        // Map the strategy to a view model using the shared method
        var result = await MapStrategyToViewModelAsync(strategy);

        return Ok(result);
    }

    /// <summary>
    /// Maps a trading bot to a strategy view model with detailed statistics using pre-fetched positions
    /// </summary>
    /// <param name="strategy">The trading bot to map</param>
    /// <param name="positionsByIdentifier">Pre-fetched positions grouped by initiator identifier</param>
    /// <returns>A view model with detailed strategy information</returns>
    private UserStrategyDetailsViewModel MapStrategyToViewModel(Bot strategy,
        Dictionary<Guid, List<Position>> positionsByIdentifier)
    {
        // Calculate ROI percentage based on PnL relative to account value
        decimal pnl = strategy.Pnl;

        // If we had initial investment amount, we could calculate ROI like:
        decimal initialInvestment = 1000; // Example placeholder, ideally should come from the account
        decimal roi = pnl != 0 ? (pnl / initialInvestment) * 100 : 0;

        // Calculate volume statistics
        decimal totalVolume = strategy.Volume;
        decimal volumeLast24h = strategy.Volume;

        // Calculate win/loss statistics
        (int wins, int losses) = (strategy.TradeWins, strategy.TradeLosses);

        int winRate = wins + losses > 0 ? (wins * 100) / (wins + losses) : 0;
        // Calculate ROI for last 24h
        decimal roiLast24h = strategy.Roi;

        // Get positions for this strategy from pre-fetched data
        var positions = positionsByIdentifier.TryGetValue(strategy.Identifier, out var strategyPositions)
            ? strategyPositions
            : new List<Position>();

        return new UserStrategyDetailsViewModel
        {
            Name = strategy.Name,
            State = strategy.Status,
            PnL = pnl,
            ROIPercentage = roi,
            ROILast24H = roiLast24h,
            Runtime = strategy.StartupTime,
            WinRate = winRate,
            TotalVolumeTraded = totalVolume,
            VolumeLast24H = volumeLast24h,
            Wins = wins,
            Losses = losses,
            Positions = positions,
            Identifier = strategy.Identifier,
            WalletBalances = new Dictionary<DateTime, decimal>(),
        };
    }

    /// <summary>
    /// Maps a trading bot to a strategy view model with detailed statistics
    /// </summary>
    /// <param name="strategy">The trading bot to map</param>
    /// <returns>A view model with detailed strategy information</returns>
    private async Task<UserStrategyDetailsViewModel> MapStrategyToViewModelAsync(Bot strategy)
    {
        // Calculate ROI percentage based on PnL relative to account value
        decimal pnl = strategy.Pnl;

        // If we had initial investment amount, we could calculate ROI like:
        decimal initialInvestment = 1000; // Example placeholder, ideally should come from the account
        decimal roi = pnl != 0 ? (pnl / initialInvestment) * 100 : 0;

        // Calculate volume statistics
        decimal totalVolume = strategy.Volume;
        decimal volumeLast24h = strategy.Volume;

        // Calculate win/loss statistics
        (int wins, int losses) = (strategy.TradeWins, strategy.TradeLosses);

        int winRate = wins + losses > 0 ? (wins * 100) / (wins + losses) : 0;
        // Calculate ROI for last 24h
        decimal roiLast24h = strategy.Roi;

        // Fetch positions associated with this bot
        var positions = await _tradingService.GetPositionsByInitiatorIdentifierAsync(strategy.Identifier);

        return new UserStrategyDetailsViewModel
        {
            Name = strategy.Name,
            State = strategy.Status,
            PnL = pnl,
            ROIPercentage = roi,
            ROILast24H = roiLast24h,
            Runtime = strategy.StartupTime,
            WinRate = winRate,
            TotalVolumeTraded = totalVolume,
            VolumeLast24H = volumeLast24h,
            Wins = wins,
            Losses = losses,
            Positions = positions.ToList(),
            Identifier = strategy.Identifier,
            WalletBalances = new Dictionary<DateTime, decimal>(),
        };
    }

    /// <summary>
    /// Retrieves a summary of platform activity across all agents (platform-level data only)
    /// Uses Orleans grain for efficient caching and real-time updates
    /// </summary>
    /// <returns>A summary of platform activity without individual agent details</returns>
    [HttpGet("GetPlatformSummary")]
    public async Task<ActionResult<PlatformSummaryViewModel>> GetPlatformSummary()
    {
        try
        {
            // Get the platform summary grain
            var platformSummaryGrain = _grainFactory.GetGrain<IPlatformSummaryGrain>("platform-summary");

            // Get the platform summary from the grain (handles caching and real-time updates)
            var abstractionsSummary = await platformSummaryGrain.GetPlatformSummaryAsync();

            // Convert to API ViewModel
            var summary = abstractionsSummary.ToApiViewModel();

            return Ok(summary);
        }
        catch (Exception ex)
        {
            // Log the error and return a fallback response
            // In production, you might want to return cached data or partial data
            return StatusCode(500, $"Error retrieving platform summary: {ex.Message}");
        }
    }


    /// <summary>
    /// Retrieves a paginated list of agent summaries for the agent index page
    /// </summary>
    /// <param name="page">Page number (defaults to 1)</param>
    /// <param name="pageSize">Number of items per page (defaults to 10, max 100)</param>
    /// <param name="sortBy">Field to sort by (TotalPnL, TotalROI, Wins, Losses, AgentName, CreatedAt, UpdatedAt)</param>
    /// <param name="sortOrder">Sort order - "asc" or "desc" (defaults to "desc")</param>
    /// <param name="agentNames">Optional comma-separated list of agent names to filter by</param>
    /// <returns>A paginated list of agent summaries sorted by the specified field</returns>
    [AllowAnonymous]
    [HttpGet("GetAgentIndexPaginated")]
    public async Task<ActionResult<PaginatedAgentIndexResponse>> GetAgentIndexPaginated(
        int page = 1,
        int pageSize = 10,
        SortableFields sortBy = SortableFields.TotalPnL,
        string sortOrder = "desc",
        string? agentNames = null)
    {
        // Validate pagination parameters
        if (page < 1)
        {
            return BadRequest("Page must be greater than 0");
        }

        if (pageSize < 1 || pageSize > 100)
        {
            return BadRequest("Page size must be between 1 and 100");
        }

        // Validate sort order
        if (sortOrder != "asc" && sortOrder != "desc")
        {
            return BadRequest("Sort order must be 'asc' or 'desc'");
        }

        // Parse agent names filter
        IEnumerable<string>? agentNamesList = null;
        if (!string.IsNullOrWhiteSpace(agentNames))
        {
            agentNamesList = agentNames.Split(',', StringSplitOptions.RemoveEmptyEntries)
                .Select(name => name.Trim())
                .Where(name => !string.IsNullOrWhiteSpace(name))
                .ToList();
        }

        // Get paginated results from database
        var command = new GetPaginatedAgentSummariesCommand(page, pageSize, sortBy, sortOrder, agentNamesList);
        var result = await _mediator.Send(command);
        var agentSummaries = result.Results;
        var totalCount = result.TotalCount;

        // Map to view models
        var agentSummaryViewModels = new List<AgentSummaryViewModel>();
        foreach (var agentSummary in agentSummaries)
        {
            // Calculate win rate
            int averageWinRate = 0;
            if (agentSummary.Wins + agentSummary.Losses > 0)
            {
                averageWinRate = (agentSummary.Wins * 100) / (agentSummary.Wins + agentSummary.Losses);
            }

            // Map to view model
            var agentSummaryViewModel = new AgentSummaryViewModel
            {
                AgentName = agentSummary.AgentName,
                TotalPnL = agentSummary.TotalPnL,
                TotalROI = agentSummary.TotalROI,
                Wins = agentSummary.Wins,
                Losses = agentSummary.Losses,
                ActiveStrategiesCount = agentSummary.ActiveStrategiesCount,
                TotalVolume = agentSummary.TotalVolume,
                TotalBalance = agentSummary.TotalBalance,
            };

            agentSummaryViewModels.Add(agentSummaryViewModel);
        }

        var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);

        var response = new PaginatedAgentIndexResponse
        {
            AgentSummaries = agentSummaryViewModels,
            TotalCount = totalCount,
            CurrentPage = page,
            PageSize = pageSize,
            TotalPages = totalPages,
            HasNextPage = page < totalPages,
            HasPreviousPage = page > 1,
            SortBy = sortBy,
            SortOrder = sortOrder,
            FilteredAgentNames = agentNames
        };

        return Ok(response);
    }

    /// <summary>
    /// Retrieves balance history for a specific agent within a date range
    /// </summary>
    /// <param name="agentName">The name of the agent to retrieve balances for</param>
    /// <param name="startDate">The start date for the balance history</param>
    /// <param name="endDate">Optional end date for the balance history (defaults to current time)</param>
    /// <returns>A list of agent balances within the specified date range</returns>
    [HttpGet("GetAgentBalances")]
    public async Task<ActionResult<AgentBalanceHistory>> GetAgentBalances(
        string agentName,
        DateTime startDate,
        DateTime? endDate = null)
    {
        var balances = await _agentService.GetAgentBalances(agentName, startDate, endDate);
        return Ok(balances);
    }

    /// <summary>
    /// Retrieves a paginated list of the best performing agents based on their total value
    /// </summary>
    /// <param name="startDate">The start date for calculating agent performance</param>
    /// <param name="endDate">Optional end date for calculating agent performance (defaults to current time)</param>
    /// <param name="page">Page number (defaults to 1)</param>
    /// <param name="pageSize">Number of items per page (defaults to 10)</param>
    /// <returns>A paginated list of agent balances and total count</returns>
    [HttpGet("GetBestAgents")]
    public async Task<ActionResult<BestAgentsResponse>> GetBestAgents(
        DateTime startDate,
        DateTime? endDate = null,
        int page = 1,
        int pageSize = 10)
    {
        var (agents, totalCount) = await _agentService.GetBestAgents(startDate, endDate, page, pageSize);

        var response = new BestAgentsResponse
        {
            Agents = agents,
            TotalCount = totalCount,
            CurrentPage = page,
            PageSize = pageSize,
            TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
        };

        return Ok(response);
    }

    /// <summary>
    /// Retrieves an array of online agent names
    /// </summary>
    /// <returns>An array of online agent names</returns>
    [HttpGet("GetOnlineAgent")]
    public async Task<ActionResult<IEnumerable<string>>> GetOnlineAgent()
    {
        const string cacheKey = "OnlineAgentNames";

        // Check if the online agent names are already cached
        var cachedAgentNames = _cacheService.GetValue<List<string>>(cacheKey);

        if (cachedAgentNames != null)
        {
            return Ok(cachedAgentNames);
        }

        // Get only online agent names
        var onlineAgentNames = await _mediator.Send(new GetOnlineAgentNamesCommand());

        // Cache the results for 2 minutes
        _cacheService.SaveValue(cacheKey, onlineAgentNames, TimeSpan.FromMinutes(2));

        return Ok(onlineAgentNames);
    }

    /// <summary>
    /// Maps a ScenarioRequest to a domain Scenario object.
    /// </summary>
    /// <param name="scenarioRequest">The scenario request to map.</param>
    /// <returns>A domain Scenario object.</returns>
    private Scenario MapScenarioRequestToScenario(ScenarioRequest scenarioRequest)
    {
        var scenario = new Scenario(scenarioRequest.Name, scenarioRequest.LoopbackPeriod);

        foreach (var indicatorRequest in scenarioRequest.Indicators)
        {
            var indicator = new IndicatorBase(indicatorRequest.Name, indicatorRequest.Type)
            {
                SignalType = indicatorRequest.SignalType,
                MinimumHistory = indicatorRequest.MinimumHistory,
                Period = indicatorRequest.Period,
                FastPeriods = indicatorRequest.FastPeriods,
                SlowPeriods = indicatorRequest.SlowPeriods,
                SignalPeriods = indicatorRequest.SignalPeriods,
                Multiplier = indicatorRequest.Multiplier,
                SmoothPeriods = indicatorRequest.SmoothPeriods,
                StochPeriods = indicatorRequest.StochPeriods,
                CyclePeriods = indicatorRequest.CyclePeriods
            };

            scenario.AddIndicator(indicator);
        }

        return scenario;
    }
}