using Managing.Api.Models.Requests;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Application.ManageBot.Commands;
using Managing.Common;
using Managing.Domain.Trades;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;
using ApplicationTradingBot = Managing.Application.Bots.TradingBot;
using ApiTradingBot = Managing.Api.Models.Responses.TradingBot;

namespace Managing.Api.Controllers;

/// <summary>
/// Controller for bot operations such as starting, stopping, deleting, and managing bots.
/// Requires authorization for access and produces JSON responses.
/// </summary>
[ApiController]
[Authorize]
[Route("[controller]")]
[Produces("application/json")]
public class BotController : BaseController
{
    private readonly IMediator _mediator;
    private readonly ILogger<BotController> _logger;
    private readonly IHubContext<BotHub> _hubContext;
    private readonly IBacktester _backtester;
    private readonly IBotService _botService;
    private readonly IAccountService _accountService;
    private readonly IMoneyManagementService _moneyManagementService;

    /// <summary>
    /// Initializes a new instance of the <see cref="BotController"/> class.
    /// </summary>
    /// <param name="logger">Logger for logging information.</param>
    /// <param name="mediator">Mediator for handling commands and requests.</param>
    /// <param name="hubContext">SignalR hub context for real-time communication.</param>
    /// <param name="backtester">Backtester for running backtests on bots.</param>
    public BotController(ILogger<BotController> logger, IMediator mediator, IHubContext<BotHub> hubContext,
        IBacktester backtester, IBotService botService, IUserService userService,
        IAccountService accountService, IMoneyManagementService moneyManagementService) : base(userService)
    {
        _logger = logger;
        _mediator = mediator;
        _hubContext = hubContext;
        _backtester = backtester;
        _botService = botService;
        _accountService = accountService;
        _moneyManagementService = moneyManagementService;
    }

    /// <summary>
    /// Checks if the current authenticated user owns the account associated with the specified bot or account name
    /// </summary>
    /// <param name="botName">The name of the bot to check</param>
    /// <param name="accountName">Optional account name to check when creating a new bot</param>
    /// <returns>True if the user owns the account, False otherwise</returns>
    private async Task<bool> UserOwnsBotAccount(string botName, string accountName = null)
    {
        try
        {
            var user = await GetUser();
            if (user == null)
                return false;

            // For new bot creation, check if the user owns the account provided in the request
            if (!string.IsNullOrEmpty(accountName))
            {
                var accountService = HttpContext.RequestServices.GetRequiredService<IAccountService>();
                var account = await accountService.GetAccount(accountName, true, false);
                // Compare the user names
                return account != null && account.User != null && account.User.Name == user.Name;
            }

            // For existing bots, check if the user owns the bot's account
            var activeBots = _botService.GetActiveBots();
            var bot = activeBots.FirstOrDefault(b => b.Name == botName);
            if (bot == null)
                return true; // Bot doesn't exist yet, so no ownership conflict

            var botAccountService = HttpContext.RequestServices.GetRequiredService<IAccountService>();
            var botAccount = await botAccountService.GetAccount(bot.AccountName, true, false);
            // Compare the user names
            return botAccount != null && botAccount.User != null && botAccount.User.Name == user.Name;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking if user owns bot account");
            return false;
        }
    }

    /// <summary>
    /// Starts a bot with the specified parameters.
    /// </summary>
    /// <param name="request">The request containing bot start parameters.</param>
    /// <returns>A string indicating the result of the start operation.</returns>
    [HttpPost]
    [Route("Start")]
    public async Task<ActionResult<string>> Start(StartBotRequest request)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(request.BotName, request.AccountName))
            {
                return Forbid("You don't have permission to start this bot");
            }

            // Trigger error if money management is not provided
            if (string.IsNullOrEmpty(request.MoneyManagementName))
            {
                return BadRequest("Money management name is required");
            }

            var user = await GetUser();
            var moneyManagement = await _moneyManagementService.GetMoneyMangement(user, request.MoneyManagementName);
            if (moneyManagement == null)
            {
                return BadRequest("Money management not found");
            }

            // Validate initialTradingBalance
            if (request.InitialTradingBalance <= Constants.GMX.Config.MinimumPositionAmount)
            {
                return BadRequest(
                    $"Initial trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}");
            }

            var result = await _mediator.Send(new StartBotCommand(request.BotType, request.BotName, request.Ticker,
                request.Scenario, request.Timeframe, request.AccountName, request.MoneyManagementName, user,
                request.IsForWatchOnly, request.InitialTradingBalance));

            await NotifyBotSubscriberAsync();
            return Ok(result);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error starting bot");
            return StatusCode(500, $"Error starting bot: {ex.Message}");
        }
    }

    /// <summary>
    /// Stops a bot specified by type and name.
    /// </summary>
    /// <param name="botType">The type of the bot to stop.</param>
    /// <param name="botName">The name of the bot to stop.</param>
    /// <returns>A string indicating the result of the stop operation.</returns>
    [HttpGet]
    [Route("Stop")]
    public async Task<ActionResult<string>> Stop(BotType botType, string botName)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(botName))
            {
                return Forbid("You don't have permission to stop this bot");
            }

            var result = await _mediator.Send(new StopBotCommand(botType, botName));
            _logger.LogInformation($"{botType} type called {botName} is now {result}");

            await NotifyBotSubscriberAsync();

            return Ok(result);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error stopping bot");
            return StatusCode(500, $"Error stopping bot: {ex.Message}");
        }
    }

    /// <summary>
    /// Deletes a bot specified by name.
    /// </summary>
    /// <param name="botName">The name of the bot to delete.</param>
    /// <returns>A boolean indicating the result of the delete operation.</returns>
    [HttpDelete]
    [Route("Delete")]
    public async Task<ActionResult<bool>> Delete(string botName)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(botName))
            {
                return Forbid("You don't have permission to delete this bot");
            }

            var result = await _botService.DeleteBot(botName);
            await NotifyBotSubscriberAsync();
            return Ok(result);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error deleting bot");
            return StatusCode(500, $"Error deleting bot: {ex.Message}");
        }
    }

    /// <summary>
    /// Stops all active bots.
    /// </summary>
    /// <returns>A string summarizing the results of the stop operations for all bots.</returns>
    [HttpPost("stop-all")]
    public async Task<string> StopAll()
    {
        // This method should be restricted to only stop bots owned by the current user
        var user = await GetUser();
        if (user == null)
            return "No authenticated user found";

        try
        {
            var bots = await GetBotList();
            // Filter to only include bots owned by the current user
            var userBots = new List<ApiTradingBot>();

            foreach (var bot in bots)
            {
                var account = await _accountService.GetAccount(bot.AccountName, true, false);
                // Compare the user names
                if (account != null && account.User != null && account.User.Name == user.Name)
                {
                    userBots.Add(bot);
                }
            }

            foreach (var bot in userBots)
            {
                await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));
                await _hubContext.Clients.All.SendAsync("SendNotification",
                    $"Bot {bot.Name} paused by {user.Name}.", "Info");
            }

            await NotifyBotSubscriberAsync();
            return "All your bots have been stopped successfully!";
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error stopping all bots");
            return $"Error stopping bots: {ex.Message}";
        }
    }

    /// <summary>
    /// Restarts a bot specified by type and name.
    /// </summary>
    /// <param name="botType">The type of the bot to restart.</param>
    /// <param name="botName">The name of the bot to restart.</param>
    /// <returns>A string indicating the result of the restart operation.</returns>
    [HttpGet]
    [Route("Restart")]
    public async Task<ActionResult<string>> Restart(BotType botType, string botName)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(botName))
            {
                return Forbid("You don't have permission to restart this bot");
            }

            var result = await _mediator.Send(new RestartBotCommand(botType, botName));
            _logger.LogInformation($"{botType} type called {botName} is now {result}");

            await NotifyBotSubscriberAsync();

            return Ok(result);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error restarting bot");
            return StatusCode(500, $"Error restarting bot: {ex.Message}");
        }
    }

    /// <summary>
    /// Restarts all active bots.
    /// </summary>
    /// <returns>A string summarizing the results of the restart operations for all bots.</returns>
    [HttpPost("restart-all")]
    public async Task<string> RestartAll()
    {
        var user = await GetUser();
        if (user == null)
            return "No authenticated user found";

        try
        {
            var bots = await GetBotList();
            // Filter to only include bots owned by the current user
            var userBots = new List<ApiTradingBot>();
            var accountService = HttpContext.RequestServices.GetRequiredService<IAccountService>();

            foreach (var bot in bots)
            {
                var account = await accountService.GetAccount(bot.AccountName, true, false);
                // Compare the user names
                if (account != null && account.User != null && account.User.Name == user.Name)
                {
                    userBots.Add(bot);
                }
            }

            foreach (var bot in userBots)
            {
                // We can't directly restart a bot with just BotType and Name
                // Instead, stop the bot and then retrieve the backup to start it again
                await _mediator.Send(new StopBotCommand(bot.BotType, bot.Name));

                // Get the saved bot backup
                var backup = _botService.GetBotBackup(bot.Name);
                if (backup != null)
                {
                    _botService.StartBotFromBackup(backup);
                    await _hubContext.Clients.All.SendAsync("SendNotification",
                        $"Bot {bot.Name} restarted by {user.Name}.", "Info");
                }
            }

            await NotifyBotSubscriberAsync();
            return "All your bots have been restarted successfully!";
        }
        catch (Exception e)
        {
            _logger.LogError(e, "Failed to restart all bots");
            return $"Error restarting bots: {e.Message}";
        }
    }

    /// <summary>
    /// Toggles the watching status of a bot specified by name.
    /// </summary>
    /// <param name="botName">The name of the bot to toggle watching status.</param>
    /// <returns>A string indicating the new watching status of the bot.</returns>
    [HttpGet]
    [Route("ToggleIsForWatching")]
    public async Task<ActionResult<string>> ToggleIsForWatching(string botName)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(botName))
            {
                return Forbid("You don't have permission to modify this bot");
            }

            var result = await _mediator.Send(new ToggleIsForWatchingCommand(botName));
            _logger.LogInformation($"{botName} bot is now {result}");

            await NotifyBotSubscriberAsync();

            return Ok(result);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error toggling bot watching status");
            return StatusCode(500, $"Error toggling bot watching status: {ex.Message}");
        }
    }

    /// <summary>
    /// Retrieves a list of active bots.
    /// </summary>
    /// <returns>A list of active trading bots.</returns>
    [HttpGet]
    public async Task<List<ApiTradingBot>> GetActiveBots()
    {
        return await GetBotList();
    }

    /// <summary>
    /// Retrieves a list of active bots by sending a command to the mediator.
    /// </summary>
    /// <returns>A list of trading bots.</returns>
    private async Task<List<ApiTradingBot>> GetBotList()
    {
        var result = await _mediator.Send(new GetActiveBotsCommand());
        var list = new List<ApiTradingBot>();

        foreach (var item in result)
        {
            list.Add(new ApiTradingBot
            {
                Status = item.GetStatus(),
                Name = item.Name,
                Signals = item.Signals.ToList(),
                Positions = item.Positions,
                Candles = item.Candles.DistinctBy(c => c.Date).ToList(),
                WinRate = item.GetWinRate(),
                ProfitAndLoss = item.GetProfitAndLoss(),
                Timeframe = item.Timeframe,
                Ticker = item.Ticker,
                Scenario = item.ScenarioName,
                IsForWatchingOnly = item.IsForWatchingOnly,
                BotType = item.BotType,
                AccountName = item.AccountName,
                MoneyManagement = item.MoneyManagement
            });
        }

        return list;
    }

    /// <summary>
    /// Notifies subscribers about the current list of bots via SignalR.
    /// </summary>
    private async Task NotifyBotSubscriberAsync()
    {
        var botsList = await GetBotList();
        await _hubContext.Clients.All.SendAsync("BotsSubscription", botsList);
    }

    /// <summary>
    /// Manually opens a position for a specified bot with the given parameters.
    /// </summary>
    /// <param name="request">The request containing position parameters.</param>
    /// <returns>A response indicating the result of the operation.</returns>
    [HttpPost]
    [Route("OpenPosition")]
    public async Task<ActionResult<Position>> OpenPositionManually([FromBody] OpenPositionManuallyRequest request)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(request.BotName))
            {
                return Forbid("You don't have permission to open positions for this bot");
            }

            var activeBots = _botService.GetActiveBots();
            var bot = activeBots.FirstOrDefault(b => b.Name == request.BotName) as ApplicationTradingBot;

            if (bot == null)
            {
                return NotFound($"Bot {request.BotName} not found or is not a trading bot");
            }

            if (bot.GetStatus() != BotStatus.Up.ToString())
            {
                return BadRequest($"Bot {request.BotName} is not running");
            }

            var position = await bot.OpenPositionManually(
                request.Direction
            );

            await NotifyBotSubscriberAsync();
            return Ok(position);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error opening position manually");
            return StatusCode(500, $"Error opening position: {ex.Message}");
        }
    }

    /// <summary>
    /// Closes a specific position for a trading bot
    /// </summary>
    /// <param name="request">The request containing the position close parameters</param>
    /// <returns>The closed position or an error</returns>
    [HttpPost]
    [Route("ClosePosition")]
    public async Task<ActionResult<Position>> ClosePosition([FromBody] ClosePositionRequest request)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(request.BotName))
            {
                return Forbid("You don't have permission to close positions for this bot");
            }

            var activeBots = _botService.GetActiveBots();
            var bot = activeBots.FirstOrDefault(b => b.Name == request.BotName) as ApplicationTradingBot;

            if (bot == null)
            {
                return NotFound($"Bot {request.BotName} not found or is not a trading bot");
            }

            if (bot.GetStatus() != BotStatus.Up.ToString())
            {
                return BadRequest($"Bot {request.BotName} is not running");
            }

            // Find the position to close
            var position = bot.Positions.FirstOrDefault(p => p.Identifier == request.PositionId);
            if (position == null)
            {
                return NotFound($"Position with ID {request.PositionId} not found for bot {request.BotName}");
            }

            // Find the signal associated with this position
            var signal = bot.Signals.FirstOrDefault(s => s.Identifier == position.SignalIdentifier);
            if (signal == null)
            {
                return NotFound($"Signal not found for position {request.PositionId}");
            }

            // Get current price
            var lastCandle = bot.OptimizedCandles.LastOrDefault();
            if (lastCandle == null)
            {
                return BadRequest("Cannot get current price to close position");
            }

            // Close the position at market price
            await bot.CloseTrade(signal, position, position.Open, lastCandle.Close, true);

            await NotifyBotSubscriberAsync();
            return Ok(position);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error closing position");
            return StatusCode(500, $"Error closing position: {ex.Message}");
        }
    }
}

/// <summary>
/// Request model for closing a position
/// </summary>
public class ClosePositionRequest
{
    /// <summary>
    /// The name of the bot
    /// </summary>
    public string BotName { get; set; }

    /// <summary>
    /// The ID of the position to close
    /// </summary>
    public string PositionId { get; set; }
}