using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Domain.Backtests;
using Managing.Domain.Bots;
using Managing.Domain.MoneyManagements;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;

namespace Managing.Api.Controllers;

/// <summary>
/// Controller for managing backtest operations.
/// Provides endpoints for creating, retrieving, and deleting backtests.
/// Returns complete backtest configurations for easy bot deployment.
/// Requires authorization for access.
/// </summary>
[ApiController]
[Authorize]
[Route("[controller]")]
[Produces("application/json")]
public class BacktestController : BaseController
{
    private readonly IHubContext<BotHub> _hubContext;
    private readonly IBacktester _backtester;
    private readonly IScenarioService _scenarioService;
    private readonly IAccountService _accountService;
    private readonly IMoneyManagementService _moneyManagementService;

    /// <summary>
    /// Initializes a new instance of the <see cref="BacktestController"/> class.
    /// </summary>
    /// <param name="hubContext">The SignalR hub context for real-time communication.</param>
    /// <param name="backtester">The service for backtesting strategies.</param>
    /// <param name="scenarioService">The service for managing scenarios.</param>
    /// <param name="accountService">The service for account management.</param>
    /// <param name="moneyManagementService">The service for money management strategies.</param>
    public BacktestController(
        IHubContext<BotHub> hubContext,
        IBacktester backtester,
        IScenarioService scenarioService,
        IAccountService accountService,
        IMoneyManagementService moneyManagementService,
        IUserService userService) : base(userService)
    {
        _hubContext = hubContext;
        _backtester = backtester;
        _scenarioService = scenarioService;
        _accountService = accountService;
        _moneyManagementService = moneyManagementService;
    }

    /// <summary>
    /// Retrieves all backtests for the authenticated user.
    /// Each backtest includes the complete TradingBotConfig for easy bot deployment.
    /// </summary>
    /// <returns>A list of backtests with complete configurations.</returns>
    [HttpGet]
    public async Task<ActionResult<IEnumerable<Backtest>>> Backtests()
    {
        var user = await GetUser();
        return Ok(await _backtester.GetBacktestsByUser(user));
    }

    /// <summary>
    /// Retrieves a specific backtest by ID for the authenticated user.
    /// This endpoint will also populate the candles for visualization and includes
    /// the complete TradingBotConfig that can be used to start a new bot.
    /// </summary>
    /// <param name="id">The ID of the backtest to retrieve.</param>
    /// <returns>The requested backtest with populated candle data and complete configuration.</returns>
    [HttpGet("{id}")]
    public async Task<ActionResult<Backtest>> Backtest(string id)
    {
        var user = await GetUser();
        var backtest = _backtester.GetBacktestByIdForUser(user, id);

        if (backtest == null)
        {
            return NotFound($"Backtest with ID {id} not found or doesn't belong to the current user.");
        }

        return Ok(backtest);
    }

    /// <summary>
    /// Deletes a specific backtest by ID for the authenticated user.
    /// </summary>
    /// <param name="id">The ID of the backtest to delete.</param>
    /// <returns>An ActionResult indicating the outcome of the operation.</returns>
    [HttpDelete]
    public async Task<ActionResult> DeleteBacktest(string id)
    {
        var user = await GetUser();
        return Ok(_backtester.DeleteBacktestByUser(user, id));
    }

    /// <summary>
    /// Runs a backtest with the specified configuration.
    /// The returned backtest includes a complete TradingBotConfig that preserves all
    /// settings including nullable MaxPositionTimeHours for easy bot deployment.
    /// </summary>
    /// <param name="request">The backtest request containing configuration and parameters.</param>
    /// <returns>The result of the backtest with complete configuration.</returns>
    [HttpPost]
    [Route("Run")]
    public async Task<ActionResult<Backtest>> Run([FromBody] RunBacktestRequest request)
    {
        if (request?.Config == null)
        {
            return BadRequest("Backtest configuration is required");
        }

        if (string.IsNullOrEmpty(request.Config.AccountName))
        {
            return BadRequest("Account name is required");
        }

        if (string.IsNullOrEmpty(request.Config.ScenarioName))
        {
            return BadRequest("Scenario name is required");
        }

        if (string.IsNullOrEmpty(request.MoneyManagementName) && request.MoneyManagement == null)
        {
            return BadRequest("Either money management name or money management object is required");
        }

        try
        {
            Backtest backtestResult = null;
            var scenario = _scenarioService.GetScenario(request.Config.ScenarioName);
            var account = await _accountService.GetAccount(request.Config.AccountName, true, false);
            var user = await GetUser();

            if (scenario == null)
                return BadRequest("No scenario found");

            // Get money management
            MoneyManagement moneyManagement;
            if (!string.IsNullOrEmpty(request.MoneyManagementName))
            {
                moneyManagement = await _moneyManagementService.GetMoneyMangement(user, request.MoneyManagementName);
                if (moneyManagement == null)
                    return BadRequest("Money management not found");
            }
            else
            {
                moneyManagement = request.MoneyManagement;
                moneyManagement?.FormatPercentage();
            }

            // Update config with money management
            var backtestConfig = new TradingBotConfig
            {
                AccountName = request.Config.AccountName,
                MoneyManagement = moneyManagement,
                Ticker = request.Config.Ticker,
                ScenarioName = request.Config.ScenarioName,
                Timeframe = request.Config.Timeframe,
                IsForWatchingOnly = request.WatchOnly,
                BotTradingBalance = request.Balance,
                BotType = request.Config.BotType,
                IsForBacktest = true,
                CooldownPeriod = request.Config.CooldownPeriod,
                MaxLossStreak = request.Config.MaxLossStreak,
                MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
                FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
                FlipPosition = request.Config.FlipPosition,
                Name = request.Config.Name ?? $"Backtest-{request.Config.ScenarioName}-{DateTime.UtcNow:yyyyMMdd-HHmmss}",
                CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable
            };

            switch (request.Config.BotType)
            {
                case BotType.SimpleBot:
                    // SimpleBot backtest not implemented yet
                    break;
                case BotType.ScalpingBot:
                case BotType.FlippingBot:
                    backtestResult = await _backtester.RunTradingBotBacktest(
                        backtestConfig,
                        request.StartDate,
                        request.EndDate,
                        user,
                        request.Save,
                        null);
                    break;
            }

            await NotifyBacktesingSubscriberAsync(backtestResult);

            return Ok(backtestResult);
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Error running backtest: {ex.Message}");
        }
    }

    /// <summary>
    /// Notifies subscribers about the backtesting results via SignalR.
    /// </summary>
    /// <param name="backtesting">The backtest result to notify subscribers about.</param>
    private async Task NotifyBacktesingSubscriberAsync(Backtest backtesting)
    {
        if (backtesting != null)
        {
            await _hubContext.Clients.All.SendAsync("BacktestsSubscription", backtesting);
        }
    }
}

/// <summary>
/// Request model for running a backtest
/// </summary>
public class RunBacktestRequest
{
    /// <summary>
    /// The trading bot configuration to use for the backtest
    /// </summary>
    public TradingBotConfig Config { get; set; }

    /// <summary>
    /// The start date for the backtest
    /// </summary>
    public DateTime StartDate { get; set; }

    /// <summary>
    /// The end date for the backtest
    /// </summary>
    public DateTime EndDate { get; set; }

    /// <summary>
    /// The starting balance for the backtest
    /// </summary>
    public decimal Balance { get; set; }

    /// <summary>
    /// Whether to only watch the backtest without executing trades
    /// </summary>
    public bool WatchOnly { get; set; } = false;

    /// <summary>
    /// Whether to save the backtest results
    /// </summary>
    public bool Save { get; set; } = false;

    /// <summary>
    /// The name of the money management strategy to use (optional if MoneyManagement is provided)
    /// </summary>
    public string? MoneyManagementName { get; set; }

    /// <summary>
    /// The money management strategy details (optional if MoneyManagementName is provided)
    /// </summary>
    public MoneyManagement? MoneyManagement { get; set; }
}