using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Grains;
using Managing.Application.Abstractions.Services;
using Managing.Application.Trading.Commands;
using Managing.Common;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Trades;
using static Managing.Common.Enums;

namespace Managing.Application.Trading.Handlers
{
    public class OpenPositionCommandHandler(
        IExchangeService exchangeService,
        IAccountService accountService,
        ITradingService tradingService,
        IGrainFactory? grainFactory = null)
        : ICommandHandler<OpenPositionRequest, Position>
    {
        public async Task<Position> Handle(OpenPositionRequest request)
        {
            var account = await accountService.GetAccount(request.AccountName, hideSecrets: false, getBalance: false);


            var initiator = request.IsForPaperTrading ? PositionInitiator.PaperTrading : request.Initiator;
            var position = new Position(Guid.NewGuid(), request.AccountName, request.Direction,
                request.Ticker,
                request.MoneyManagement,
                initiator, request.Date, request.User);

            if (!string.IsNullOrEmpty(request.SignalIdentifier))
            {
                position.SignalIdentifier = request.SignalIdentifier;
            }

            position.InitiatorIdentifier = request.InitiatorIdentifier;

            // Always use BotTradingBalance directly as the balance to risk
            // Round to 2 decimal places to prevent precision errors
            decimal balanceToRisk = Math.Round(request.AmountToTrade, 2, MidpointRounding.ToZero);

            // Minimum check
            if (balanceToRisk < Constants.GMX.Config.MinimumPositionAmount)
            {
                throw new Exception(
                    $"Bot trading balance of {balanceToRisk} USD is less than the minimum {Constants.GMX.Config.MinimumPositionAmount} USD required to trade");
            }

            var price = request.IsForPaperTrading && request.Price.HasValue
                ? request.Price.Value
                : await exchangeService.GetPrice(account, request.Ticker, DateTime.Now);
            var quantity = balanceToRisk / price;

            var openPrice = request.IsForPaperTrading || request.Price.HasValue
                ? request.Price.Value
                : await exchangeService.GetBestPrice(account, request.Ticker, price, quantity, request.Direction);

            // Determine SL/TP Prices
            var stopLossPrice = RiskHelpers.GetStopLossPrice(request.Direction, openPrice, request.MoneyManagement);
            var takeProfitPrice = RiskHelpers.GetTakeProfitPrice(request.Direction, openPrice, request.MoneyManagement);

            var trade = await exchangeService.OpenTrade(
                account,
                request.Ticker,
                request.Direction,
                openPrice,
                quantity,
                request.MoneyManagement.Leverage,
                TradeType.Limit,
                isForPaperTrading: request.IsForPaperTrading,
                currentDate: request.Date,
                stopLossPrice: stopLossPrice,
                takeProfitPrice: takeProfitPrice);

            position.Open = trade;

            var closeDirection = request.Direction == TradeDirection.Long
                ? TradeDirection.Short
                : TradeDirection.Long;

            // Stop loss
            position.StopLoss = exchangeService.BuildEmptyTrade(
                request.Ticker,
                stopLossPrice,
                position.Open.Quantity,
                closeDirection,
                request.MoneyManagement.Leverage,
                TradeType.StopLoss,
                request.Date,
                TradeStatus.Requested);

            // Take profit
            position.TakeProfit1 = exchangeService.BuildEmptyTrade(
                request.Ticker,
                takeProfitPrice,
                quantity,
                closeDirection,
                request.MoneyManagement.Leverage,
                TradeType.TakeProfit,
                request.Date,
                TradeStatus.Requested);

            position.Status = IsOpenTradeHandled(position.Open.Status, account.Exchange)
                ? position.Status
                : PositionStatus.Rejected;

            if (!request.IsForPaperTrading)
            {
                await tradingService.InsertPositionAsync(position);

                // Notify platform summary about the opened position
                try
                {
                    var platformGrain = grainFactory?.GetGrain<IPlatformSummaryGrain>("platform-summary");
                    if (platformGrain != null)
                    {
                        var positionOpenedEvent = new PositionOpenedEvent
                        {
                            PositionId = position.Identifier,
                            Ticker = position.Ticker,
                            Volume = position.Open.Price * position.Open.Quantity * position.Open.Leverage,
                            Direction = position.OriginDirection
                        };

                        await platformGrain.OnPositionOpenedAsync(positionOpenedEvent);
                    }
                }
                catch (Exception)
                {
                    // Log error but don't fail the position creation
                    // This is a non-critical notification
                }
            }

            return position;
        }

        private static bool IsOpenTradeHandled(TradeStatus tradeStatus, TradingExchanges exchange)
        {
            return tradeStatus == TradeStatus.Filled
                   || (exchange == TradingExchanges.Evm && tradeStatus == TradeStatus.Requested);
        }
    }
}