using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Services;
using Managing.Domain.Scenarios;
using Managing.Domain.Strategies;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using static Managing.Common.Enums;
using System.Collections.Generic;
using Managing.Domain.Users;

namespace Managing.Application.Scenarios
{
    public class ScenarioService : IScenarioService
    {
        private readonly ILogger<ScenarioService> _logger;
        private readonly ITradingService _tradingService;

        public ScenarioService(ILogger<ScenarioService> logger, ITradingService tradingService)
        {
            _logger = logger;
            _tradingService = tradingService;
        }

        public Scenario CreateScenario(string name, List<string> strategies, int? loopbackPeriod = 1)
        {
            var scenario = new Scenario(name, loopbackPeriod);

            foreach (var strategy in strategies)
            {
                scenario.AddStrategy(_tradingService.GetStrategyByName(strategy));
            }

            try
            {
                _tradingService.InsertScenario(scenario);
            }
            catch (MongoCommandException ex)
            {
                _logger.LogError(ex.Message);
                throw new Exception("Cannot create scenario");
            }

            return scenario;
        }

        public Strategy CreateStrategy(
            StrategyType type,
            string name,
            int? period = null,
            int? fastPeriods = null,
            int? slowPeriods = null,
            int? signalPeriods = null,
            double? multiplier = null,
            int? stochPeriods = null,
            int? smoothPeriods = null,
            int? cyclePeriods = null)
        {
            var strategy = ScenarioHelpers.BuildStrategy(
                type,
                name,
                period,
                fastPeriods,
                slowPeriods,
                signalPeriods,
                multiplier,
                stochPeriods,
                smoothPeriods,
                cyclePeriods);
            _tradingService.InsertStrategy(strategy);
            return strategy;
        }

        public IEnumerable<Scenario> GetScenarios()
        {
            return _tradingService.GetScenarios();
        }

        public Scenario GetScenario(string name)
        {
            return _tradingService.GetScenarioByName(name);
        }

        public IEnumerable<Strategy> GetStrategies()
        {
            return _tradingService.GetStrategies();
        }

        public bool DeleteScenario(string name)
        {
            try
            {
                _tradingService.DeleteScenario(name);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return false;
            }
        }

        public bool DeleteStrategy(string name)
        {
            try
            {
                _tradingService.DeleteStrategy(name);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return false;
            }
        }

        public bool DeleteStrategies()
        {
            try
            {
                _tradingService.DeleteStrategies();
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        public bool DeleteScenarios()
        {
            try
            {
                _tradingService.DeleteScenarios();
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        public bool UpdateScenario(string name, List<string> strategies, int? loopbackPeriod)
        {
            try
            {
                var scenario = _tradingService.GetScenarioByName(name);
                scenario.Strategies.Clear();
                foreach (var strategy in strategies)
                {
                    scenario.AddStrategy(_tradingService.GetStrategyByName(strategy));
                }

                scenario.LoopbackPeriod = loopbackPeriod ?? 1;
                _tradingService.UpdateScenario(scenario);
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return false;
            }
        }

        public bool UpdateStrategy(StrategyType strategyType, string name, int? period, int? fastPeriods,
            int? slowPeriods,
            int? signalPeriods, double? multiplier, int? stochPeriods, int? smoothPeriods, int? cyclePeriods)
        {
            try
            {
                var strategy = _tradingService.GetStrategyByName(name);
                strategy.Type = strategyType;
                strategy.Period = period;
                strategy.FastPeriods = fastPeriods;
                strategy.SlowPeriods = slowPeriods;
                strategy.SignalPeriods = signalPeriods;
                strategy.Multiplier = multiplier;
                strategy.StochPeriods = stochPeriods;
                strategy.SmoothPeriods = smoothPeriods;
                strategy.CyclePeriods = cyclePeriods;
                _tradingService.UpdateStrategy(strategy);
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return false;
            }
        }

        // User-specific methods implementation

        public IEnumerable<Scenario> GetScenariosByUser(User user)
        {
            var scenarios = _tradingService.GetScenarios();
            return scenarios.Where(s => s.User?.Name == user.Name);
        }

        public Scenario CreateScenarioForUser(User user, string name, List<string> strategies, int? loopbackPeriod = 1)
        {
            var scenario = new Scenario(name, loopbackPeriod ?? 1)
            {
                User = user
            };
            
            foreach (var strategyName in strategies)
            {
                var strategy = _tradingService.GetStrategyByName(strategyName);
                if (strategy != null && strategy.User?.Name == user.Name)
                {
                    scenario.AddStrategy(strategy);
                }
            }

            _tradingService.InsertScenario(scenario);
            return scenario;
        }

        public IEnumerable<Strategy> GetStrategiesByUser(User user)
        {
            var strategies = _tradingService.GetStrategies();
            return strategies.Where(s => s.User?.Name == user.Name);
        }

        public bool DeleteStrategyByUser(User user, string name)
        {
            var strategy = _tradingService.GetStrategyByName(name);
            if (strategy != null && strategy.User?.Name == user.Name)
            {
                _tradingService.DeleteStrategy(strategy.Name);
                return true;
            }
            return false;
        }

        public bool DeleteScenarioByUser(User user, string name)
        {
            var scenario = _tradingService.GetScenarioByName(name);
            if (scenario != null && scenario.User?.Name == user.Name)
            {
                _tradingService.DeleteScenario(scenario.Name);
                return true;
            }
            return false;
        }

        public Scenario GetScenarioByUser(User user, string name)
        {
            var scenario = _tradingService.GetScenarioByName(name);
            return scenario != null && scenario.User?.Name == user.Name ? scenario : null;
        }

        public Strategy CreateStrategyForUser(User user, StrategyType type, string name, int? period = null, 
            int? fastPeriods = null, int? slowPeriods = null, int? signalPeriods = null, 
            double? multiplier = null, int? stochPeriods = null, int? smoothPeriods = null, 
            int? cyclePeriods = null)
        {
            // Create a new strategy using the existing implementation
            var strategy = CreateStrategy(type, name, period, fastPeriods, slowPeriods, signalPeriods, 
                multiplier, stochPeriods, smoothPeriods, cyclePeriods);
            
            // Set the user
            strategy.User = user;
            
            // Update the strategy to save the user property
            _tradingService.UpdateStrategy(strategy);
            
            return strategy;
        }

        public bool DeleteStrategiesByUser(User user)
        {
            try 
            {
                var strategies = GetStrategiesByUser(user);
                
                foreach (var strategy in strategies)
                {
                    _tradingService.DeleteStrategy(strategy.Name);
                }
                
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return false;
            }
        }

        public bool DeleteScenariosByUser(User user)
        {
            try
            {
                var scenarios = GetScenariosByUser(user);
                
                foreach (var scenario in scenarios)
                {
                    _tradingService.DeleteScenario(scenario.Name);
                }
                
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                return false;
            }
        }

        public bool UpdateScenarioByUser(User user, string name, List<string> strategies, int? loopbackPeriod)
        {
            var scenario = _tradingService.GetScenarioByName(name);
            if (scenario == null || scenario.User?.Name != user.Name)
            {
                return false;
            }

            scenario.Strategies.Clear();
            scenario.LoopbackPeriod = loopbackPeriod ?? 1;

            foreach (var strategyName in strategies)
            {
                var strategy = _tradingService.GetStrategyByName(strategyName);
                if (strategy != null && strategy.User?.Name == user.Name)
                {
                    scenario.AddStrategy(strategy);
                }
            }

            _tradingService.UpdateScenario(scenario);
            return true;
        }

        public bool UpdateStrategyByUser(User user, StrategyType strategyType, string name, int? period, 
            int? fastPeriods, int? slowPeriods, int? signalPeriods, double? multiplier, 
            int? stochPeriods, int? smoothPeriods, int? cyclePeriods)
        {
            var strategy = _tradingService.GetStrategyByName(name);
            if (strategy == null || strategy.User?.Name != user.Name)
            {
                return false;
            }

            // Use the existing update strategy logic
            var result = UpdateStrategy(strategyType, name, period, fastPeriods, slowPeriods, 
                signalPeriods, multiplier, stochPeriods, smoothPeriods, cyclePeriods);
        
            return result;
        }
    }
}