using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Orleans;
using Managing.Core;
using Managing.Domain.Bots;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;

namespace Managing.Application.Bots.Grains;

/// <summary>
/// Grain that initializes bot reminders on application startup and periodically checks for running bots.
/// Fetches all running bots and pings them to ensure their reminders are properly registered.
/// This grain ensures that only one instance runs the initialization process
/// even in multi-silo environments.
/// Uses custom trading placement with load balancing and built-in fallback.
/// </summary>
[TradingPlacement] // Use custom trading placement with load balancing
public class BotReminderInitializerGrain : Grain, IBotReminderInitializerGrain, IRemindable
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly ILogger<BotReminderInitializerGrain> _logger;

    private const string CheckRunningBotsReminderName = "CheckRunningBotsReminder";

    public BotReminderInitializerGrain(
        IServiceScopeFactory scopeFactory,
        ILogger<BotReminderInitializerGrain> logger)
    {
        _scopeFactory = scopeFactory;
        _logger = logger;
    }

    /// <summary>
    /// Initializes bot reminders by fetching all running bots and pinging them
    /// to ensure their reminders are properly registered.
    /// </summary>
    public async Task InitializeBotRemindersAsync()
    {
        try
        {
            _logger.LogInformation(
                "BotReminderInitializerGrain starting - fetching running bots to reactivate reminders");

            // Get all running bots from the database
            var runningBots = await GetRunningBotsAsync();

            if (!runningBots.Any())
            {
                _logger.LogInformation("No running bots found to reactivate");
                return;
            }

            _logger.LogInformation("Found {Count} running bots to reactivate", runningBots.Count());

            var botRegistry = GrainFactory.GetGrain<ILiveBotRegistryGrain>(0);

            // Ping each running bot to reactivate it and ensure reminders are registered
            var tasks = runningBots.Select(async bot =>
            {
                try
                {
                    _logger.LogDebug("Reactivating bot {BotId} ({BotName})", bot.Identifier, bot.Name);

                    // First, update the bot status in the registry to Running
                    await botRegistry.UpdateBotStatus(bot.Identifier, BotStatus.Running);

                    _logger.LogDebug("Updated registry status to Running for bot {BotId} ({BotName})", bot.Identifier,
                        bot.Name);

                    // Then ping the bot to reactivate it
                    var grain = GrainFactory.GetGrain<ILiveTradingBotGrain>(bot.Identifier);
                    var success = await grain.PingAsync();

                    if (success)
                    {
                        _logger.LogDebug("Successfully reactivated bot {BotId} ({BotName})", bot.Identifier, bot.Name);
                    }
                    else
                    {
                        _logger.LogWarning("Ping failed for bot {BotId} ({BotName})", bot.Identifier, bot.Name);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Failed to reactivate bot {BotId} ({BotName})", bot.Identifier, bot.Name);
                    SentrySdk.CaptureException(ex);
                }
            });

            // Wait for all pings to complete
            await Task.WhenAll(tasks);

            // Register a reminder to check running bots every hour
            // Start immediately and repeat every hour
            await this.RegisterOrUpdateReminder(
                CheckRunningBotsReminderName,
                TimeSpan.FromHours(1), // Start in 1 hour
                TimeSpan.FromHours(1)); // Repeat every hour

            _logger.LogInformation("BotReminderInitializerGrain completed - processed {Count} running bots",
                runningBots.Count());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error during BotReminderInitializerGrain initialization");
            SentrySdk.CaptureException(ex);
        }
    }

    /// <summary>
    /// Handles reminder callbacks for periodic bot checking
    /// </summary>
    public Task ReceiveReminder(string reminderName, TickStatus status)
    {
        if (reminderName == CheckRunningBotsReminderName)
        {
            _logger.LogDebug("Received hourly reminder to check running bots");
            return InitializeBotRemindersAsync();
        }

        return Task.CompletedTask;
    }

    /// <summary>
    /// Fetches all bots with Running status from the database
    /// </summary>
    private async Task<IEnumerable<Bot>> GetRunningBotsAsync()
    {
        try
        {
            return await ServiceScopeHelpers.WithScopedService<IBotService, IEnumerable<Bot>>(
                _scopeFactory,
                async botService => await botService.GetBotsByStatusAsync(BotStatus.Running));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to fetch running bots from database");
            return Enumerable.Empty<Bot>();
        }
    }
}