using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Application.ManageBot.Commands;
using Managing.Application.Workers.Abstractions;
using Managing.Api.Models.Responses;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Statistics;
using Managing.Domain.Trades;
using Managing.Domain.Users;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System.Linq;
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>
[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 IHubContext<CandleHub> _hubContext;
    private readonly IMediator _mediator;

    /// <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="hubContext">SignalR hub context for real-time communication.</param>
    /// <param name="mediator">Mediator for handling commands and queries.</param>
    public DataController(
        IExchangeService exchangeService,
        IAccountService accountService,
        ICacheService cacheService,
        IStatisticService statisticService,
        IHubContext<CandleHub> hubContext,
        IMediator mediator)
    {
        _exchangeService = exchangeService;
        _accountService = accountService;
        _cacheService = cacheService;
        _statisticService = statisticService;
        _hubContext = hubContext;
        _mediator = mediator;
    }

    /// <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<Ticker[]>> GetTickers(Timeframe timeframe)
    {
        var cacheKey = string.Concat(timeframe.ToString());
        var tickers = _cacheService.GetValue<List<Ticker>>(cacheKey);

        if (tickers == null || tickers.Count == 0)
        {
            tickers = await _exchangeService.GetTickers(timeframe);
            _cacheService.SaveValue(cacheKey, tickers, TimeSpan.FromHours(2));
        }

        return Ok(tickers);
    }

    /// <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 ActionResult<SpotlightOverview> GetSpotlight()
    {
        var overview = _cacheService.GetOrSave(nameof(SpotlightOverview),
            () => { return _statisticService.GetLastSpotlight(DateTime.Now.AddDays(-2)); }, TimeSpan.FromMinutes(2));

        if (overview?.Spotlights.Count < overview?.ScenarioCount || overview == null)
        {
            overview = _statisticService.GetLastSpotlight(DateTime.Now.AddDays(-2));
        }

        return Ok(overview);
    }

    /// <summary>
    /// Retrieves candle data for a given exchange, ticker, start date, and timeframe.
    /// </summary>
    /// <param name="exchange">The exchange to retrieve candles from.</param>
    /// <param name="ticker">The ticker symbol to retrieve candles for.</param>
    /// <param name="startDate">The start date for the candle data.</param>
    /// <param name="timeframe">The timeframe for the candle data.</param>
    /// <returns>A list of <see cref="Candle"/> objects.</returns>
    [Authorize]
    [HttpGet("GetCandles")]
    public async Task<ActionResult<List<Candle>>> GetCandles(TradingExchanges exchange, Ticker ticker,
        DateTime startDate, Timeframe timeframe)
    {
        return Ok(await _exchangeService.GetCandlesInflux(exchange, ticker, startDate, timeframe));
    }

    /// <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 GetActiveBotsCommand());
        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()
    {
        const string cacheKey = "TopStrategies";

        // Check if the top strategies are already cached
        var cachedStrategies = _cacheService.GetValue<TopStrategiesViewModel>(cacheKey);

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

        // Get active bots
        var activeBots = await _mediator.Send(new GetActiveBotsCommand());

        // Calculate PnL for each bot once and store in a list of tuples
        var botsWithPnL = activeBots
            .Select(bot => new { Bot = bot, PnL = bot.GetProfitAndLoss() })
            .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
                })
                .ToList()
        };

        // Cache the result for 10 minutes
        _cacheService.SaveValue(cacheKey, topStrategies, TimeSpan.FromMinutes(10));

        return Ok(topStrategies);
    }

    /// <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)
    {
        string cacheKey = $"UserStrategies_{agentName}";
        
        // Check if the user strategy details are already cached
        var cachedDetails = _cacheService.GetValue<List<UserStrategyDetailsViewModel>>(cacheKey);
        
        if (cachedDetails != null && cachedDetails.Count > 0)
        {
            return Ok(cachedDetails);
        }
        
        // Get all strategies for the specified user
        var userStrategies = await _mediator.Send(new GetUserStrategiesCommand(agentName));
        
        // Convert to detailed view model with additional information
        var result = userStrategies.Select(strategy => MapStrategyToViewModel(strategy)).ToList();
        
        // Cache the results for 5 minutes
        _cacheService.SaveValue(cacheKey, result, TimeSpan.FromMinutes(5));
        
        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)
    {
        string cacheKey = $"UserStrategy_{agentName}_{strategyName}";
        
        // Check if the user strategy details are already cached
        var cachedDetails = _cacheService.GetValue<UserStrategyDetailsViewModel>(cacheKey);
        
        if (cachedDetails != null)
        {
            return Ok(cachedDetails);
        }
        
        // 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 = MapStrategyToViewModel(strategy);
        
        // Cache the results for 5 minutes
        _cacheService.SaveValue(cacheKey, result, TimeSpan.FromMinutes(5));
        
        return Ok(result);
    }
    
    /// <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 UserStrategyDetailsViewModel MapStrategyToViewModel(ITradingBot strategy)
    {
        // Get the runtime directly from the bot
        TimeSpan runtimeSpan = strategy.GetRuntime();
        
        // Get the startup time from the bot's internal property
        // If bot is not running, we use MinValue as a placeholder
        DateTime startupTime = DateTime.MinValue;
        if (strategy is Bot bot && bot.StartupTime != DateTime.MinValue)
        {
            startupTime = bot.StartupTime;
        }
        
        // Calculate ROI percentage based on PnL relative to account value
        decimal pnl = strategy.GetProfitAndLoss();
        
        // 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 = TradingBox.GetTotalVolumeTraded(strategy.Positions);
        decimal volumeLast24h = TradingBox.GetLast24HVolumeTraded(strategy.Positions);
        
        // Calculate win/loss statistics
        (int wins, int losses) = TradingBox.GetWinLossCount(strategy.Positions);
        
        // Calculate ROI for last 24h
        decimal roiLast24h = TradingBox.GetLast24HROI(strategy.Positions);
        
        return new UserStrategyDetailsViewModel
        {
            Name = strategy.Name,
            StrategyName = strategy.ScenarioName,
            State = strategy.GetStatus() == BotStatus.Up.ToString() ? "RUNNING" : 
                    strategy.GetStatus() == BotStatus.Down.ToString() ? "STOPPED" : "UNUSED",
            PnL = pnl,
            ROIPercentage = roi,
            ROILast24H = roiLast24h,
            Runtime = startupTime,
            WinRate = strategy.GetWinRate(),
            TotalVolumeTraded = totalVolume,
            VolumeLast24H = volumeLast24h,
            Wins = wins,
            Losses = losses,
            Positions = strategy.Positions.OrderByDescending(p => p.Date).ToList() // Include sorted positions with most recent first
        };
    }

    /// <summary>
    /// Retrieves a summary of platform activity across all agents
    /// </summary>
    /// <param name="timeFilter">Time filter to apply (24H, 3D, 1W, 1M, 1Y, Total)</param>
    /// <returns>A summary of platform activity including per-agent statistics</returns>
    [HttpGet("GetPlatformSummary")]
    public async Task<ActionResult<PlatformSummaryViewModel>> GetPlatformSummary(string timeFilter = "Total")
    {
        // Validate time filter
        var validTimeFilters = new[] { "24H", "3D", "1W", "1M", "1Y", "Total" };
        if (!validTimeFilters.Contains(timeFilter))
        {
            timeFilter = "Total"; // Default to Total if invalid
        }
        
        string cacheKey = $"PlatformSummary_{timeFilter}";
        
        // Check if the platform summary is already cached
        var cachedSummary = _cacheService.GetValue<PlatformSummaryViewModel>(cacheKey);
        
        if (cachedSummary != null)
        {
            return Ok(cachedSummary);
        }
        
        // Get all agents and their strategies
        var agentsWithStrategies = await _mediator.Send(new GetAllAgentsCommand(timeFilter));
        
        // Create the platform summary
        var summary = new PlatformSummaryViewModel
        {
            TotalAgents = agentsWithStrategies.Count,
            TotalActiveStrategies = agentsWithStrategies.Values.Sum(list => list.Count),
            TimeFilter = timeFilter
        };
        
        // Calculate total platform metrics
        decimal totalPlatformPnL = 0;
        decimal totalPlatformVolume = 0;
        decimal totalPlatformVolumeLast24h = 0;
        
        // Create summaries for each agent
        foreach (var agent in agentsWithStrategies)
        {
            var user = agent.Key;
            var strategies = agent.Value;
            
            if (strategies.Count == 0)
            {
                continue; // Skip agents with no strategies
            }
            
            // Combine all positions from all strategies
            var allPositions = strategies.SelectMany<ITradingBot, Position>(s => s.Positions).ToList();
            
            // Calculate agent metrics
            decimal totalPnL = TradingBox.GetPnLInTimeRange(allPositions, timeFilter);
            decimal pnlLast24h = TradingBox.GetPnLInTimeRange(allPositions, "24H");
            
            decimal totalROI = TradingBox.GetROIInTimeRange(allPositions, timeFilter);
            decimal roiLast24h = TradingBox.GetROIInTimeRange(allPositions, "24H");
            
            (int wins, int losses) = TradingBox.GetWinLossCountInTimeRange(allPositions, timeFilter);
            
            // Calculate trading volumes
            decimal totalVolume = TradingBox.GetTotalVolumeTraded(allPositions);
            decimal volumeLast24h = TradingBox.GetLast24HVolumeTraded(allPositions);
            
            // Calculate win rate
            int averageWinRate = 0;
            if (wins + losses > 0)
            {
                averageWinRate = (wins * 100) / (wins + losses);
            }
            
            // Add to agent summaries
            var agentSummary = new AgentSummaryViewModel
            {
                Username = user.Name,
                TotalPnL = totalPnL,
                PnLLast24h = pnlLast24h,
                TotalROI = totalROI,
                ROILast24h = roiLast24h,
                Wins = wins,
                Losses = losses,
                AverageWinRate = averageWinRate,
                ActiveStrategiesCount = strategies.Count,
                TotalVolume = totalVolume,
                VolumeLast24h = volumeLast24h
            };
            
            summary.AgentSummaries.Add(agentSummary);
            
            // Add to platform totals
            totalPlatformPnL += totalPnL;
            totalPlatformVolume += totalVolume;
            totalPlatformVolumeLast24h += volumeLast24h;
        }
        
        // Set the platform totals
        summary.TotalPlatformPnL = totalPlatformPnL;
        summary.TotalPlatformVolume = totalPlatformVolume;
        summary.TotalPlatformVolumeLast24h = totalPlatformVolumeLast24h;
        
        // Sort agent summaries by total PnL (highest first)
        summary.AgentSummaries = summary.AgentSummaries.OrderByDescending(a => a.TotalPnL).ToList();
        
        // Cache the results for 5 minutes
        _cacheService.SaveValue(cacheKey, summary, TimeSpan.FromMinutes(5));
        
        return Ok(summary);
    }
}