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

namespace Managing.Application.Trading;

public class ClosePositionCommandHandler(
    IExchangeService exchangeService,
    IAccountService accountService,
    ITradingService tradingService,
    ILogger<ClosePositionCommandHandler> logger = null)
    : ICommandHandler<ClosePositionCommand, Position>
{
    public async Task<Position> Handle(ClosePositionCommand request)
    {
        try
        {
            // Get Trade
            var account = await accountService.GetAccount(request.Position.AccountName, false, false);
            if (request.Position == null)
            {
                _ = exchangeService.CancelOrder(account, request.Position.Ticker).Result;
                return request.Position;
            }

            var isForPaperTrading = request.IsForBacktest;

            var lastPrice = request.Position.Initiator == PositionInitiator.PaperTrading
                ? request.ExecutionPrice.GetValueOrDefault()
                : exchangeService.GetPrice(account, request.Position.Ticker, DateTime.UtcNow);

            // Check if position still open 
            if (!request.IsForBacktest)
            {
                var p = (await exchangeService.GetBrokerPositions(account))
                    .FirstOrDefault(x => x.Ticker == request.Position.Ticker);

                // Position not available on the broker, so be sure to update the status
                if (p == null)
                {
                    request.Position.Status = PositionStatus.Finished;
                    request.Position.ProfitAndLoss =
                        TradingBox.GetProfitAndLoss(request.Position, request.Position.Open.Quantity, lastPrice,
                            request.Position.Open.Leverage);
                    tradingService.UpdatePosition(request.Position);
                    return request.Position;
                }
            }


            var closeRequestedOrders =
                isForPaperTrading || (await exchangeService.CancelOrder(account, request.Position.Ticker));

            // Close market 
            var closedPosition =
                await exchangeService.ClosePosition(account, request.Position, lastPrice, isForPaperTrading);

            if (closeRequestedOrders || closedPosition.Status == (TradeStatus.PendingOpen | TradeStatus.Filled))
            {
                request.Position.Status = PositionStatus.Finished;
                request.Position.ProfitAndLoss =
                    TradingBox.GetProfitAndLoss(request.Position, closedPosition.Quantity, lastPrice,
                        request.Position.Open.Leverage);

                if (!request.IsForBacktest)
                    tradingService.UpdatePosition(request.Position);
            }

            return request.Position;
        }
        catch (Exception ex)
        {
            logger?.LogError(ex, "Error closing position: {Message} \n Stacktrace : {StackTrace}", ex.Message,
                ex.StackTrace);

            throw;
        }
    }
}