using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Domain.Backtests;
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.
/// 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.
    /// </summary>
    /// <returns>A list of backtests.</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.
    /// </summary>
    /// <param name="id">The ID of the backtest to retrieve.</param>
    /// <returns>The requested backtest with populated candle data.</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 parameters.
    /// </summary>
    /// <param name="accountName">The name of the account to use for the backtest.</param>
    /// <param name="botType">The type of bot to use for the backtest.</param>
    /// <param name="ticker">The ticker symbol to backtest.</param>
    /// <param name="scenarioName">The name of the scenario to use for the backtest.</param>
    /// <param name="timeframe">The timeframe for the backtest.</param>
    /// <param name="watchOnly">Whether to only watch the backtest without executing trades.</param>
    /// <param name="startDate">The start date for the backtest.</param>
    /// <param name="endDate">The end date for the backtest.</param>
    /// <param name="balance">The starting balance for the backtest.</param>
    /// <param name="moneyManagementName">The name of the money management strategy to use.</param>
    /// <param name="moneyManagement">The money management strategy details, if not using a named strategy.</param>
    /// <param name="save">Whether to save the backtest results.</param>
    /// <returns>The result of the backtest.</returns>
    [HttpPost]
    [Route("Run")]
    public async Task<ActionResult<Backtest>> Run(string accountName,
        BotType botType,
        Ticker ticker,
        string scenarioName,
        Timeframe timeframe,
        bool watchOnly,
        decimal balance,
        string moneyManagementName,
        DateTime startDate,
        DateTime endDate,
        MoneyManagement? moneyManagement = null,
        bool save = false)
    {
        if (string.IsNullOrEmpty(accountName))
        {
            throw new ArgumentException($"'{nameof(accountName)}' cannot be null or empty.", nameof(accountName));
        }

        if (string.IsNullOrEmpty(scenarioName))
        {
            throw new ArgumentException($"'{nameof(scenarioName)}' cannot be null or empty.", nameof(scenarioName));
        }

        if (string.IsNullOrEmpty(moneyManagementName) && moneyManagement == null)
        {
            throw new ArgumentException(
                $"'{nameof(moneyManagementName)}' and '{nameof(moneyManagement)}' cannot be null or empty.",
                nameof(moneyManagementName));
        }

        Backtest backtestResult = null;
        var scenario = _scenarioService.GetScenario(scenarioName);
        var account = await _accountService.GetAccount(accountName, true, false);
        var user = await GetUser();

        if (!string.IsNullOrEmpty(moneyManagementName) && moneyManagement is null)
        {
            moneyManagement = await _moneyManagementService.GetMoneyMangement(user, moneyManagementName);
        }
        else
        {
            moneyManagement.FormatPercentage();
        }

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

        switch (botType)
        {
            case BotType.SimpleBot:
                break;
            case BotType.ScalpingBot:
                backtestResult = await _backtester.RunScalpingBotBacktest(
                    account,
                    moneyManagement,
                    ticker,
                    scenario,
                    timeframe,
                    balance,
                    startDate,
                    endDate,
                    user,
                    watchOnly,
                    save);
                break;
            case BotType.FlippingBot:
                backtestResult = await _backtester.RunFlippingBotBacktest(
                    account,
                    moneyManagement,
                    ticker,
                    scenario,
                    timeframe,
                    balance,
                    startDate,
                    endDate,
                    user,
                    watchOnly,
                    save);
                break;
        }

        await NotifyBacktesingSubscriberAsync(backtestResult);

        return Ok(backtestResult);
    }

    /// <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);
        }
    }
}