using Managing.Api.Models.Requests;
using Managing.Api.Models.Responses;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Application.ManageBot.Commands;
using Managing.Application.Shared;
using Managing.Common;
using Managing.Core;
using Managing.Domain.Accounts;
using Managing.Domain.Backtests;
using Managing.Domain.Bots;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Scenarios;
using Managing.Domain.Strategies;
using Managing.Domain.Trades;
using Managing.Domain.Users;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using static Managing.Common.Enums;

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;
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly IAdminConfigurationService _adminService;

    /// <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>
    /// <param name="accountService"></param>
    /// <param name="moneyManagementService"></param>
    /// <param name="botService"></param>
    /// <param name="userService"></param>
    /// <param name="scopeFactory"></param>
    public BotController(ILogger<BotController> logger, IMediator mediator, IHubContext<BotHub> hubContext,
        IBacktester backtester, IBotService botService, IUserService userService,
        IAccountService accountService, IMoneyManagementService moneyManagementService,
        IServiceScopeFactory scopeFactory, IAdminConfigurationService adminService) : base(userService)
    {
        _logger = logger;
        _mediator = mediator;
        _hubContext = hubContext;
        _backtester = backtester;
        _botService = botService;
        _accountService = accountService;
        _moneyManagementService = moneyManagementService;
        _scopeFactory = scopeFactory;
        _adminService = adminService;
    }

    /// <summary>
    /// Checks if the current authenticated user owns the account associated with the specified bot or account name
    /// </summary>
    /// <param name="identifier">The identifier 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 or is admin, False otherwise</returns>
    private async Task<bool> UserOwnsBotAccount(Guid identifier, string accountName = null)
    {
        try
        {
            var user = await GetUser();
            if (user == null)
                return false;

            // Admin users can access all bots
            if (_adminService.IsUserAdmin(user.Name))
                return true;

            if (identifier != default)
            {
                // For existing bots, check if the user owns the bot's account
                var bot = await _botService.GetBotByIdentifier(identifier);

                if (bot != null)
                {
                    return bot.User != null && bot.User.Name == user.Name;
                }
            }


            // For new bot creation, check if the user owns the account provided in the request
            if (!string.IsNullOrEmpty(accountName))
            {
                var account = await ServiceScopeHelpers.WithScopedService<IAccountService, Account>(_scopeFactory,
                    async accountService => { return await accountService.GetAccount(accountName, true, false); });

                // Compare the user names
                return account != null && account.User != null && account.User.Name == user.Name;
            }

            return false;
        }
        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
        {
            var (config, user) = await ValidateAndPrepareBotRequest(request, "start");

            var result = await _mediator.Send(new StartBotCommand(config, user, false)); // createOnly = false

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

    /// <summary>
    /// Saves a bot configuration without starting it.
    /// </summary>
    /// <param name="request">The request containing bot configuration parameters.</param>
    /// <returns>A string indicating the result of the save operation.</returns>
    [HttpPost]
    [Route("Save")]
    public async Task<ActionResult<string>> Save(SaveBotRequest request)
    {
        try
        {
            var (config, user) = await ValidateAndPrepareBotRequest(request, "save");

            var result = await _mediator.Send(new StartBotCommand(config, user, true)); // createOnly = true

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


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

            var result = await _mediator.Send(new StopBotCommand(identifier));
            _logger.LogInformation($"Bot identifier {identifier} 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>
    /// Stops all active bots for the connected user.
    /// </summary>
    /// <returns>A boolean indicating the result of the stop all operation.</returns>
    [HttpGet]
    [Route("StopAll")]
    public async Task<ActionResult<bool>> StopAll()
    {
        try
        {
            var user = await GetUser();
            if (user == null)
            {
                return Unauthorized("User not found");
            }

            var result = await _mediator.Send(new StopAllUserBotsCommand(user));
            
            if (result)
            {
                await NotifyBotSubscriberAsync();
                _logger.LogInformation($"All bots stopped successfully for user {user.Name}");
            }
            else
            {
                _logger.LogWarning($"Failed to stop all bots for user {user.Name}");
            }

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

    /// <summary>
    /// Deletes a bot specified by name.
    /// </summary>
    /// <param name="identifier">The identifier 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(Guid identifier)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(identifier))
            {
                return Forbid("You don't have permission to delete this bot");
            }

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


    /// <summary>
    /// Restarts a bot specified by type and name.
    /// </summary>
    /// <param name="identifier">The identifier 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(Guid identifier)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(identifier))
            {
                return Forbid("You don't have permission to restart this bot");
            }

            var result = await _mediator.Send(new RestartBotCommand(identifier));
            _logger.LogInformation($"Bot with identifier {identifier} is now {result}");

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

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

    /// <summary>
    /// Retrieves a list of bots by status.
    /// </summary>
    /// <param name="status">The status to filter bots by (None, Down, Up)</param>
    /// <returns>A list of trading bots with the specified status.</returns>
    [HttpGet]
    [Route("ByStatus/{status}")]
    public async Task<List<TradingBotResponse>> GetBotsByStatus(BotStatus status)
    {
        return await GetBotsByStatusAsync(status);
    }

    /// <summary>
    /// Retrieves a list of saved bots (status None) for the current user.
    /// </summary>
    /// <returns>A list of saved trading bots for the current user.</returns>
    [HttpGet]
    [Route("GetMySavedBots")]
    public async Task<List<TradingBotResponse>> GetMySavedBots()
    {
        try
        {
            var user = await GetUser();
            if (user == null)
            {
                return new List<TradingBotResponse>();
            }

            var result = await _mediator.Send(new GetBotsByUserAndStatusCommand(user.Id, BotStatus.Saved));
            return MapBotsToTradingBotResponse(result);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting saved bots for user");
            return new List<TradingBotResponse>();
        }
    }

    /// <summary>
    /// Retrieves a paginated list of bots with filtering and sorting capabilities.
    /// </summary>
    /// <param name="pageNumber">Page number (1-based). Default is 1.</param>
    /// <param name="pageSize">Number of items per page. Default is 10, maximum is 100.</param>
    /// <param name="status">Filter by bot status. If null, returns bots of all statuses.</param>
    /// <param name="name">Filter by bot name (partial match, case-insensitive). If null, no name filtering is applied.</param>
    /// <param name="ticker">Filter by ticker (partial match, case-insensitive). If null, no ticker filtering is applied.</param>
    /// <param name="agentName">Filter by agent name (partial match, case-insensitive). If null, no agent name filtering is applied.</param>
    /// <param name="sortBy">Sort field. Valid values: "Name", "Ticker", "Status", "CreateDate", "StartupTime", "Pnl", "WinRate", "AgentName". Default is "CreateDate".</param>
    /// <param name="sortDirection">Sort direction. Default is "Desc".</param>
    /// <returns>A paginated response containing trading bots</returns>
    [HttpGet]
    [Route("Paginated")]
    public async Task<PaginatedResponse<TradingBotResponse>> GetBotsPaginated(
        int pageNumber = 1,
        int pageSize = 10,
        BotStatus? status = null,
        string? name = null,
        string? ticker = null,
        string? agentName = null,
        string sortBy = "CreateDate",
        string sortDirection = "Desc")
    {
        try
        {
            // Validate parameters
            if (pageNumber < 1)
            {
                pageNumber = 1;
            }

            if (pageSize < 1 || pageSize > 100)
            {
                pageSize = Math.Min(Math.Max(pageSize, 1), 100);
            }

            // Get paginated bots from service
            var (bots, totalCount) = await _botService.GetBotsPaginatedAsync(
                pageNumber,
                pageSize,
                status,
                name,
                ticker,
                agentName,
                sortBy,
                sortDirection);

            // Map to response objects
            var tradingBotResponses = MapBotsToTradingBotResponse(bots);

            // Calculate pagination metadata
            var totalPages = (int)Math.Ceiling((double)totalCount / pageSize);
            var hasPreviousPage = pageNumber > 1;
            var hasNextPage = pageNumber < totalPages;

            return new PaginatedResponse<TradingBotResponse>
            {
                Items = tradingBotResponses,
                TotalCount = totalCount,
                PageNumber = pageNumber,
                PageSize = pageSize,
                TotalPages = totalPages,
                HasPreviousPage = hasPreviousPage,
                HasNextPage = hasNextPage
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting paginated bots");
            return new PaginatedResponse<TradingBotResponse>
            {
                Items = new List<TradingBotResponse>(),
                TotalCount = 0,
                PageNumber = pageNumber,
                PageSize = pageSize,
                TotalPages = 0,
                HasPreviousPage = false,
                HasNextPage = false
            };
        }
    }

    /// <summary>
    /// Retrieves a list of bots by status by sending a command to the mediator.
    /// </summary>
    /// <param name="status">The status to filter bots by</param>
    /// <returns>A list of trading bots.</returns>
    private async Task<List<TradingBotResponse>> GetBotsByStatusAsync(BotStatus status)
    {
        var result = await _mediator.Send(new GetBotsByStatusCommand(status));
        return MapBotsToTradingBotResponse(result);
    }

    /// <summary>
    /// Maps a collection of Bot entities to TradingBotResponse objects.
    /// </summary>
    /// <param name="bots">The collection of bots to map</param>
    /// <returns>A list of TradingBotResponse objects</returns>
    private static List<TradingBotResponse> MapBotsToTradingBotResponse(IEnumerable<Bot> bots)
    {
        var list = new List<TradingBotResponse>();

        foreach (var item in bots)
        {
            list.Add(new TradingBotResponse
            {
                Status = item.Status.ToString(),
                WinRate = (item.TradeWins + item.TradeLosses) != 0
                    ? item.TradeWins / (item.TradeWins + item.TradeLosses)
                    : 0,
                ProfitAndLoss = item.Pnl,
                Identifier = item.Identifier.ToString(),
                AgentName = item.User.AgentName,
                CreateDate = item.CreateDate,
                StartupTime = item.StartupTime,
                Name = item.Name,
                Ticker = item.Ticker,
            });
        }

        return list;
    }

    /// <summary>
    /// Notifies subscribers about the current list of bots via SignalR.
    /// </summary>
    private async Task NotifyBotSubscriberAsync()
    {
        var botsList = await GetBotsByStatusAsync(BotStatus.Running);
        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.Identifier))
            {
                return Forbid("You don't have permission to open positions for this bot");
            }

            var bot = await _botService.GetBotByIdentifier(request.Identifier);

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

            if (bot.Status != BotStatus.Running)
            {
                return BadRequest($"Bot with identifier {request.Identifier} is not running");
            }

            var position = await _botService.OpenPositionManuallyAsync(request.Identifier, 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}, {ex.InnerException?.Message} or {ex.StackTrace}");
        }
    }

    /// <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.Identifier))
            {
                return Forbid("You don't have permission to close positions for this bot");
            }

            var position = await _botService.ClosePositionAsync(request.Identifier, request.PositionId);
            if (position == null)
            {
                return NotFound(
                    $"Position with ID {request.PositionId} not found for bot with identifier {request.Identifier}");
            }

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

    /// <summary>
    /// Retrieves the configuration of an existing bot.
    /// </summary>
    /// <param name="identifier">The identifier of the bot to get configuration for</param>
    /// <returns>The bot configuration</returns>
    [HttpGet]
    [Route("GetConfig/{identifier}")]
    public async Task<ActionResult<TradingBotConfig>> GetBotConfig(Guid identifier)
    {
        try
        {
            // Check if user owns the account
            if (!await UserOwnsBotAccount(identifier))
            {
                return Forbid("You don't have permission to view this bot's configuration");
            }

            var config = await _botService.GetBotConfig(identifier);
            if (config == null)
            {
                return NotFound($"Bot with identifier {identifier} not found");
            }

            return Ok(config);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting bot configuration for identifier {Identifier}", identifier);
            return StatusCode(500, $"Error getting bot configuration: {ex.Message}");
        }
    }

    /// <summary>
    /// Updates the configuration of an existing bot.
    /// </summary>
    /// <param name="request">The update request containing the bot identifier and new configuration</param>
    /// <returns>Success message</returns>
    [HttpPut]
    [Route("UpdateConfig")]
    public async Task<ActionResult<string>> UpdateBotConfig([FromBody] UpdateBotConfigRequest request)
    {
        try
        {
            var user = await GetUser();
            if (user == null)
            {
                return Unauthorized("User not found");
            }

            if (request.Identifier == Guid.Empty)
            {
                return BadRequest("Bot identifier is required");
            }

            if (request.Config == null)
            {
                return BadRequest("Bot configuration is required");
            }

            // First, check if the user owns the existing bot
            if (!await UserOwnsBotAccount(request.Identifier))
            {
                return Forbid("You don't have permission to update this bot");
            }

            // Get the existing bot to ensure it exists and get current config
            var existingBot = await _botService.GetBotByIdentifier(request.Identifier);

            if (existingBot == null)
            {
                return NotFound($"Bot with identifier '{request.Identifier}' not found");
            }

            var config = await _botService.GetBotConfig(request.Identifier);

            // If the account is being changed, verify the user owns the new account too
            if (config.AccountName != request.Config.AccountName)
            {
                if (!await UserOwnsBotAccount(request.Identifier, request.Config.AccountName))
                {
                    return Forbid("You don't have permission to use this account");
                }
            }

            // Validate and get the money management
            LightMoneyManagement moneyManagement = null;
            if (!string.IsNullOrEmpty(request.MoneyManagementName))
            {
                // Load money management by name
                var fullMoneyManagement =
                    await _moneyManagementService.GetMoneyMangement(user, request.MoneyManagementName);
                if (fullMoneyManagement == null)
                {
                    return BadRequest($"Money management '{request.MoneyManagementName}' not found");
                }

                if (fullMoneyManagement.User?.Name != user.Name)
                {
                    return Forbid("You don't have permission to use this money management");
                }

                // Convert to LightMoneyManagement
                moneyManagement = new LightMoneyManagement
                {
                    Name = fullMoneyManagement.Name,
                    Timeframe = fullMoneyManagement.Timeframe,
                    StopLoss = fullMoneyManagement.StopLoss,
                    TakeProfit = fullMoneyManagement.TakeProfit,
                    Leverage = fullMoneyManagement.Leverage
                };
            }
            else if (request.MoneyManagement != null)
            {
                // Use provided money management object
                moneyManagement = request.MoneyManagement;
                // Format percentage values if using custom money management
                moneyManagement.FormatPercentage();
            }
            else
            {
                // Use existing bot's money management if no new one is provided
                moneyManagement = config.MoneyManagement;
            }

            // Validate CloseEarlyWhenProfitable requires MaxPositionTimeHours
            if (request.Config.CloseEarlyWhenProfitable && !request.Config.MaxPositionTimeHours.HasValue)
            {
                return BadRequest("CloseEarlyWhenProfitable requires MaxPositionTimeHours to be set");
            }

            // Handle scenario - either from ScenarioRequest or ScenarioName
            Scenario scenarioForUpdate = null;
            if (request.Config.Scenario != null)
            {
                // Convert ScenarioRequest to Scenario domain object
                scenarioForUpdate = new Scenario(request.Config.Scenario.Name, request.Config.Scenario.LoopbackPeriod)
                {
                    User = user
                };

                // Convert IndicatorRequest objects to Indicator domain objects
                foreach (var indicatorRequest in request.Config.Scenario.Indicators)
                {
                    var indicator = new IndicatorBase(indicatorRequest.Name, indicatorRequest.Type)
                    {
                        SignalType = indicatorRequest.SignalType,
                        MinimumHistory = indicatorRequest.MinimumHistory,
                        Period = indicatorRequest.Period,
                        FastPeriods = indicatorRequest.FastPeriods,
                        SlowPeriods = indicatorRequest.SlowPeriods,
                        SignalPeriods = indicatorRequest.SignalPeriods,
                        Multiplier = indicatorRequest.Multiplier,
                        SmoothPeriods = indicatorRequest.SmoothPeriods,
                        StochPeriods = indicatorRequest.StochPeriods,
                        CyclePeriods = indicatorRequest.CyclePeriods,
                        User = user
                    };
                    scenarioForUpdate.AddIndicator(indicator);
                }
            }

            // Map the request to the full TradingBotConfig
            var updatedConfig = new TradingBotConfig
            {
                AccountName = request.Config.AccountName,
                MoneyManagement = moneyManagement,
                Ticker = request.Config.Ticker,
                Scenario = LightScenario.FromScenario(scenarioForUpdate), // Convert to LightScenario for Orleans
                ScenarioName = request.Config.ScenarioName, // Fallback to scenario name if scenario object not provided
                Timeframe = request.Config.Timeframe,
                IsForWatchingOnly = request.Config.IsForWatchingOnly,
                BotTradingBalance = request.Config.BotTradingBalance,
                CooldownPeriod = request.Config.CooldownPeriod,
                MaxLossStreak = request.Config.MaxLossStreak,
                MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
                FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
                CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable,
                UseSynthApi = request.Config.UseSynthApi,
                UseForPositionSizing = request.Config.UseForPositionSizing,
                UseForSignalFiltering = request.Config.UseForSignalFiltering,
                UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss,
                // Set computed/default properties
                IsForBacktest = false,
                FlipPosition = request.Config.FlipPosition,
                Name = request.Config.Name
            };

            var success = await _botService.UpdateBotConfiguration(request.Identifier, updatedConfig);

            if (success)
            {
                return Ok("Bot configuration updated successfully");
            }
            else
            {
                return BadRequest("Failed to update bot configuration");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error updating bot configuration for identifier {Identifier}", request.Identifier);
            return StatusCode(500, $"Error updating bot configuration: {ex.Message}");
        }
    }

    /// <summary>
    /// Validates and prepares the bot request by performing all necessary validations and mapping.
    /// </summary>
    /// <param name="request">The bot request to validate and prepare</param>
    /// <param name="operation">The operation being performed (start/save) for error messages</param>
    /// <returns>The prepared TradingBotConfig and User</returns>
    /// <exception cref="ArgumentException">Thrown when validation fails</exception>
    /// <exception cref="UnauthorizedAccessException">Thrown when user doesn't have permission</exception>
    private async Task<(TradingBotConfig, User)> ValidateAndPrepareBotRequest(StartBotRequest request,
        string operation)
    {
        if (request.Config == null)
        {
            throw new ArgumentException("Bot configuration is required");
        }

        // Check if user owns the account specified in the request
        if (!await UserOwnsBotAccount(Guid.Empty, request.Config.AccountName))
        {
            throw new UnauthorizedAccessException($"You don't have permission to {operation} a bot with this account");
        }

        // Validate that either money management name or object is provided
        if (string.IsNullOrEmpty(request.Config.MoneyManagementName) && request.Config.MoneyManagement == null)
        {
            throw new ArgumentException("Either money management name or money management object is required");
        }

        var cachedUser = await GetUser();
        var user = await _userService.GetUserByName(cachedUser.Name);

        if (string.IsNullOrEmpty(user.AgentName))
        {
            throw new ArgumentException(
                $"Agent name is required to {operation} a bot. Please configure your agent name in the user profile.");
        }

        // Get money management - either by name lookup or use provided object
        LightMoneyManagement moneyManagement;
        if (!string.IsNullOrEmpty(request.Config.MoneyManagementName))
        {
            moneyManagement =
                await _moneyManagementService.GetMoneyMangement(user, request.Config.MoneyManagementName);
            if (moneyManagement == null)
            {
                throw new ArgumentException("Money management not found");
            }
        }
        else
        {
            moneyManagement = Map(request.Config.MoneyManagement);
            // Format percentage values if using custom money management
            moneyManagement?.FormatPercentage();
        }

        // Validate initialTradingBalance
        if (request.Config.BotTradingBalance <= Constants.GMX.Config.MinimumPositionAmount)
        {
            throw new ArgumentException(
                $"Initial trading balance must be greater than {Constants.GMX.Config.MinimumPositionAmount}");
        }

        // Validate cooldown period
        if (request.Config.CooldownPeriod < 1)
        {
            throw new ArgumentException("Cooldown period must be at least 1 candle");
        }

        // Validate max loss streak
        if (request.Config.MaxLossStreak < 0)
        {
            throw new ArgumentException("Max loss streak cannot be negative");
        }

        // Validate max position time hours
        if (request.Config.MaxPositionTimeHours.HasValue && request.Config.MaxPositionTimeHours.Value <= 0)
        {
            throw new ArgumentException("Max position time hours must be greater than 0 if specified");
        }

        // Validate CloseEarlyWhenProfitable consistency
        if (request.Config.CloseEarlyWhenProfitable && !request.Config.MaxPositionTimeHours.HasValue)
        {
            throw new ArgumentException(
                "CloseEarlyWhenProfitable can only be enabled when MaxPositionTimeHours is set");
        }

        // Handle scenario - either from ScenarioRequest or ScenarioName
        Scenario scenario = null;
        if (request.Config.Scenario != null)
        {
            // Convert ScenarioRequest to Scenario domain object
            scenario = new Scenario(request.Config.Scenario.Name, request.Config.Scenario.LoopbackPeriod)
            {
                User = user
            };

            // Convert IndicatorRequest objects to Indicator domain objects
            foreach (var indicatorRequest in request.Config.Scenario.Indicators)
            {
                var indicator = new IndicatorBase(indicatorRequest.Name, indicatorRequest.Type)
                {
                    SignalType = indicatorRequest.SignalType,
                    MinimumHistory = indicatorRequest.MinimumHistory,
                    Period = indicatorRequest.Period,
                    FastPeriods = indicatorRequest.FastPeriods,
                    SlowPeriods = indicatorRequest.SlowPeriods,
                    SignalPeriods = indicatorRequest.SignalPeriods,
                    Multiplier = indicatorRequest.Multiplier,
                    SmoothPeriods = indicatorRequest.SmoothPeriods,
                    StochPeriods = indicatorRequest.StochPeriods,
                    CyclePeriods = indicatorRequest.CyclePeriods,
                    User = user
                };
                scenario.AddIndicator(indicator);
            }
        }

        // Map the request to the full TradingBotConfig
        var config = new TradingBotConfig
        {
            AccountName = request.Config.AccountName,
            MoneyManagement = moneyManagement,
            Ticker = request.Config.Ticker,
            Scenario = LightScenario.FromScenario(scenario), // Convert to LightScenario for Orleans
            ScenarioName = request.Config.ScenarioName, // Fallback to scenario name if scenario object not provided
            Timeframe = request.Config.Timeframe,
            IsForWatchingOnly = request.Config.IsForWatchingOnly,
            BotTradingBalance = request.Config.BotTradingBalance,
            CooldownPeriod = request.Config.CooldownPeriod,
            MaxLossStreak = request.Config.MaxLossStreak,
            MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
            FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
            CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable,
            UseSynthApi = request.Config.UseSynthApi,
            UseForPositionSizing = request.Config.UseForPositionSizing,
            UseForSignalFiltering = request.Config.UseForSignalFiltering,
            UseForDynamicStopLoss = request.Config.UseForDynamicStopLoss,
            // Set computed/default properties
            IsForBacktest = false,
            FlipPosition = request.Config.FlipPosition,
            Name = request.Config.Name
        };

        return (config, user);
    }

    public MoneyManagement Map(MoneyManagementRequest moneyManagementRequest)
    {
        return new MoneyManagement
        {
            Name = moneyManagementRequest.Name,
            StopLoss = moneyManagementRequest.StopLoss,
            TakeProfit = moneyManagementRequest.TakeProfit,
            Leverage = moneyManagementRequest.Leverage,
            Timeframe = moneyManagementRequest.Timeframe
        };
    }
}

/// <summary>
/// Request model for opening a position manually
/// </summary>
public class OpenPositionManuallyRequest
{
    /// <summary>
    /// The identifier of the bot
    /// </summary>
    public Guid Identifier { get; set; }

    /// <summary>
    /// The direction of the position
    /// </summary>
    public TradeDirection Direction { get; set; }
}

/// <summary>
/// Request model for closing a position
/// </summary>
public class ClosePositionRequest
{
    /// <summary>
    /// The identifier of the bot
    /// </summary>
    public Guid Identifier { get; set; }

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

/// <summary>
/// Request model for starting a bot
/// </summary>
public class StartBotRequest
{
    /// <summary>
    /// The trading bot configuration request with primary properties
    /// </summary>
    public TradingBotConfigRequest Config { get; set; }
}

public class SaveBotRequest : StartBotRequest
{
}