using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.ManageBot.Commands;
using Managing.Common;
using Managing.Domain.Accounts;
using MediatR;
using static Managing.Common.Enums;

namespace Managing.Application.ManageBot
{
    public class StartBotCommandHandler : IRequestHandler<StartBotCommand, BotStatus>
    {
        private readonly IAccountService _accountService;
        private readonly IGrainFactory _grainFactory;
        private readonly IBotService _botService;

        public StartBotCommandHandler(
            IAccountService accountService, IGrainFactory grainFactory, IBotService botService)
        {
            _accountService = accountService;
            _grainFactory = grainFactory;
            _botService = botService;
        }

        public async Task<BotStatus> Handle(StartBotCommand request, CancellationToken cancellationToken)
        {
            // Validate the configuration
            if (request.Config == null)
            {
                throw new ArgumentException("Bot configuration is required");
            }

            if (request.Config.Scenario == null || !request.Config.Scenario.Indicators.Any())
            {
                throw new InvalidOperationException(
                    "Scenario or indicators not loaded properly in constructor. This indicates a configuration error.");
            }

            if (request.Config.BotTradingBalance <= Constants.GMX.Config.MinimumPositionAmount)
            {
                throw new ArgumentException(
                    $"Bot trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}");
            }

            var account = await _accountService.GetAccount(request.Config.AccountName, true, true);

            if (account == null)
            {
                throw new Exception($"Account {request.Config.AccountName} not found");
            }

            // Check balances for EVM/GMX V2 accounts before starting
            if (account.Exchange == TradingExchanges.Evm || account.Exchange == TradingExchanges.GmxV2)
            {
                var balanceCheckResult = await _botService.CheckAccountBalancesAsync(account);
                if (!balanceCheckResult.IsSuccessful)
                {
                    throw new InvalidOperationException(balanceCheckResult.Message);
                }
            }

            Balance usdcBalance = null;
            // For other exchanges, keep the original USDC balance check
            if (account.Exchange != TradingExchanges.Evm && account.Exchange != TradingExchanges.GmxV2)
            {
                usdcBalance = account.Balances.FirstOrDefault(b => b.TokenName == Ticker.USDC.ToString());

                if (usdcBalance == null ||
                    usdcBalance.Value < Constants.GMX.Config.MinimumPositionAmount ||
                    usdcBalance.Value < request.Config.BotTradingBalance)
                {
                    throw new Exception(
                        $"Account {request.Config.AccountName} has no USDC balance or not enough balance");
                }
            }

            // Enforce allocation limit across all bots using the same account
            var availableAllocation = await _botService.GetAvailableAllocationUsdAsync(account, default);
            if (request.Config.BotTradingBalance > availableAllocation)
            {
                throw new InvalidOperationException(
                    $"Insufficient available allocation. Requested: {request.Config.BotTradingBalance:F2} USDC, " +
                    $"Balance : {usdcBalance?.Value:F2 ?? 0} Available: {availableAllocation:F2} USDC.");
            }

            try
            {
                var botGrain = _grainFactory.GetGrain<ILiveTradingBotGrain>(Guid.NewGuid());
                await botGrain.CreateAsync(request.Config, request.User);

                // Only start the bot if createOnly is false
                if (!request.CreateOnly)
                {
                    await botGrain.StartAsync();
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"Failed to start bot: {ex.Message}, {ex.StackTrace}");
            }

            return request.CreateOnly ? BotStatus.Saved : BotStatus.Running;
        }
    }
}