using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.Bots.Models;
using Managing.Domain.Statistics;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;

namespace Managing.Application.Bots.Grains;

public class AgentGrain : Grain, IAgentGrain, IRemindable
{
    private readonly IPersistentState<AgentGrainState> _state;
    private readonly ILogger<AgentGrain> _logger;
    private readonly IBotService _botService;
    private readonly IAgentService _agentService;
    private readonly IExchangeService _exchangeService;
    private readonly IUserService _userService;
    private readonly IAccountService _accountService;
    private readonly ITradingService _tradingService;
    private const string _updateSummaryReminderName = "UpdateAgentSummary";

    public AgentGrain(
        [PersistentState("agent-state", "agent-store")]
        IPersistentState<AgentGrainState> state,
        ILogger<AgentGrain> logger,
        IBotService botService,
        IAgentService agentService,
        IExchangeService exchangeService,
        IUserService userService,
        IAccountService accountService,
        ITradingService tradingService)
    {
        _state = state;
        _logger = logger;
        _botService = botService;
        _agentService = agentService;
        _exchangeService = exchangeService;
        _userService = userService;
        _accountService = accountService;
        _tradingService = tradingService;
    }

    public override Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("AgentGrain activated for user {UserId}", this.GetPrimaryKeyLong());
        return base.OnActivateAsync(cancellationToken);
    }

    public async Task InitializeAsync(int userId, string agentName)
    {
        _state.State.AgentName = agentName;
        await _state.WriteStateAsync();
        await UpdateSummary();
        _logger.LogInformation("Agent {UserId} initialized with name {AgentName}", userId, agentName);
        await RegisterReminderAsync();
    }

    private async Task RegisterReminderAsync()
    {
        try
        {
            // Register a reminder that fires every 5 minutes
            await this.RegisterOrUpdateReminder(_updateSummaryReminderName, TimeSpan.FromMinutes(5),
                TimeSpan.FromMinutes(1));
            _logger.LogInformation("Reminder registered for agent {UserId} to update summary every 5 minutes",
                this.GetPrimaryKeyLong());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to register reminder for agent {UserId}", this.GetPrimaryKeyLong());
        }
    }

    public async Task ReceiveReminder(string reminderName, TickStatus status)
    {
        if (reminderName == _updateSummaryReminderName)
        {
            try
            {
                _logger.LogInformation("Reminder triggered for agent {UserId} to update summary",
                    this.GetPrimaryKeyLong());
                await UpdateSummary();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating agent summary from reminder for user {UserId}",
                    this.GetPrimaryKeyLong());
            }
        }
    }

    public async Task UpdateSummary()
    {
        try
        {
            // Get all bots for this agent
            var bots = await _botService.GetBotsByIdsAsync(_state.State.BotIds);

            // Calculate aggregated statistics from bot data
            var totalPnL = bots.Sum(b => b.Pnl);
            var totalWins = bots.Sum(b => b.TradeWins);
            var totalLosses = bots.Sum(b => b.TradeLosses);

            // Calculate ROI based on total volume traded with proper division by zero handling
            var totalVolume = bots.Sum(b => b.Volume);
            decimal totalROI;

            if (totalVolume > 0)
            {
                totalROI = (totalPnL / totalVolume) * 100;
            }
            else if (totalVolume == 0 && totalPnL == 0)
            {
                // No trading activity yet
                totalROI = 0;
            }
            else if (totalVolume == 0 && totalPnL != 0)
            {
                // Edge case: PnL exists but no volume (shouldn't happen in normal cases)
                _logger.LogWarning("Agent {UserId} has PnL {PnL} but zero volume", this.GetPrimaryKeyLong(), totalPnL);
                totalROI = 0;
            }
            else
            {
                // Fallback for any other edge cases
                totalROI = 0;
            }

            // Calculate Runtime based on the farthest date from bot startup times
            DateTime? runtime = null;
            if (bots.Any())
            {
                runtime = bots.Max(b => b.StartupTime);
            }

            // Calculate total balance (USDC + open positions value)
            decimal totalBalance = 0;
            try
            {
                var userId = (int)this.GetPrimaryKeyLong();
                var user = await _userService.GetUserByIdAsync(userId);

                if (user != null)
                {
                    var userAccounts = await _accountService.GetAccountsByUserAsync(user, hideSecrets: true, true);

                    foreach (var account in userAccounts)
                    {
                        // Get USDC balance
                        var usdcBalances = await _exchangeService.GetBalances(account);
                        var usdcBalance = usdcBalances.FirstOrDefault(b => b.TokenName?.ToUpper() == "USDC")?.Amount ??
                                          0;
                        totalBalance += usdcBalance;
                    }

                    // Get positions for all bots using their GUIDs as InitiatorIdentifier
                    var botPositions =
                        await _tradingService.GetPositionsByInitiatorIdentifiersAsync(_state.State.BotIds);

                    foreach (var position in botPositions.Where(p => !p.IsFinished()))
                    {
                        totalBalance += position.Open.Price * position.Open.Quantity;
                        totalBalance += position.ProfitAndLoss?.Realized ?? 0;
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error calculating total balance for agent {UserId}", this.GetPrimaryKeyLong());
                totalBalance = 0; // Set to 0 if calculation fails
            }

            var summary = new AgentSummary
            {
                UserId = (int)this.GetPrimaryKeyLong(),
                AgentName = _state.State.AgentName,
                TotalPnL = totalPnL,
                Wins = totalWins,
                Losses = totalLosses,
                TotalROI = totalROI,
                Runtime = runtime,
                ActiveStrategiesCount = bots.Count(b => b.Status == BotStatus.Running),
                TotalVolume = totalVolume,
                TotalBalance = totalBalance,
            };

            // Save summary to database
            await _agentService.SaveOrUpdateAgentSummary(summary);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error calculating agent summary for user {UserId}", this.GetPrimaryKeyLong());
        }
    }

    public async Task RegisterBotAsync(Guid botId)
    {
        if (_state.State.BotIds.Add(botId))
        {
            await _state.WriteStateAsync();
            _logger.LogInformation("Bot {BotId} registered to Agent {UserId}", botId, this.GetPrimaryKeyLong());
        }
    }

    public async Task UnregisterBotAsync(Guid botId)
    {
        if (_state.State.BotIds.Remove(botId))
        {
            await _state.WriteStateAsync();
            _logger.LogInformation("Bot {BotId} unregistered from Agent {UserId}", botId, this.GetPrimaryKeyLong());
        }
    }
}