using Managing.Application.Abstractions.Repositories;
using Managing.Application.Synth;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Risk;
using Managing.Domain.Strategies;
using Managing.Domain.Synth.Models;
using Managing.Domain.Users;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
using Xunit.Abstractions;
using static Managing.Common.Enums;

namespace Managing.Infrastructure.Tests;

public class SynthPredictionTests
{
    private readonly ITestOutputHelper _testOutputHelper;

    public SynthPredictionTests(ITestOutputHelper testOutputHelper)
    {
        _testOutputHelper = testOutputHelper;
    }

    /// <summary>
    /// Helper method to create a test signal with realistic candle data
    /// </summary>
    private static Signal CreateTestSignal(Ticker ticker, TradeDirection direction, decimal price,
        DateTime? date = null)
    {
        var signalDate = date ?? DateTime.UtcNow;
        var candle = new Candle
        {
            Date = signalDate,
            Open = price * 0.999m,
            High = price * 1.001m,
            Low = price * 0.998m,
            Close = price,
            Volume = price * 1000m
        };

        return new Signal(
            ticker: ticker,
            direction: direction,
            confidence: Confidence.Medium, // Will be updated by validation
            candle: candle,
            date: signalDate,
            exchange: TradingExchanges.GmxV2,
            indicatorType: IndicatorType.Stc,
            signalType: SignalType.Signal,
            indicatorName: "TestIndicator",
            user: new User { Name = "TestUser" }
        );
    }

    [Fact]
    public async Task GetProbabilityOfTargetPriceAsync_ShouldReturnValidProbability_ForBTC_RealAPI()
    {
        // Arrange - Static values for testing
        const decimal currentBtcPrice = 102000m; // Current BTC price at $102k
        const decimal takeProfitPrice = currentBtcPrice * 1.02m; // 2% TP = $104,040
        const decimal stopLossPrice = currentBtcPrice * 0.99m; // 1% SL = $100,980
        const int timeHorizonHours = 24; // 24 hour forecast

        Console.WriteLine($"🚀 Starting Synth API Test for BTC at ${currentBtcPrice:N0}");

        // Create real API client and service
        var httpClient = new HttpClient();
        var logger = new TestLogger<SynthPredictionService>();
        var synthApiClient = new SynthApiClient(httpClient, new TestLogger<SynthApiClient>());
        var mockSynthRepository = new Mock<ISynthRepository>();
        var synthPredictionService = new SynthPredictionService(synthApiClient, mockSynthRepository.Object, logger);

        // Create configuration for enabled Synth API
        var config = new SynthConfiguration
        {
            IsEnabled = true,
            TopMinersCount = 5, // Use fewer miners for faster testing
            TimeIncrement = 300, // 5 minutes (supported by Synth API)
            DefaultTimeLength = timeHorizonHours * 3600, // 24 hours in seconds
            MaxLiquidationProbability = 0.10m,
            PredictionCacheDurationMinutes = 1 // Short cache for testing
        };

        // Act & Assert - Test Take Profit probability (upward movement for LONG)
        try
        {
            Console.WriteLine("🔍 Fetching Take Profit probability from Synth API...");

            var takeProfitProbability = await synthPredictionService.GetProbabilityOfTargetPriceAsync(
                asset: "BTC",
                currentPrice: currentBtcPrice,
                targetPrice: takeProfitPrice,
                timeHorizonSeconds: timeHorizonHours * 3600,
                isLongPosition: false, // For TP, we want upward movement (opposite of liquidation direction)
                config: config);

            Console.WriteLine($"🎯 Take Profit Analysis (2% gain):");
            Console.WriteLine($"Current Price: ${currentBtcPrice:N0}");
            Console.WriteLine($"Target Price: ${takeProfitPrice:N0}");
            Console.WriteLine($"Probability: {takeProfitProbability:P2}");

            Assert.True(takeProfitProbability >= 0m && takeProfitProbability <= 1m,
                "Take profit probability should be between 0 and 1");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"❌ Take Profit test failed: {ex.Message}");
            Console.WriteLine("⚠️ Skipping Take Profit test due to API issue");
        }

        // Act & Assert - Test Stop Loss probability (downward movement for LONG)
        try
        {
            Console.WriteLine("\n🔍 Fetching Stop Loss probability from Synth API...");

            var stopLossProbability = await synthPredictionService.GetProbabilityOfTargetPriceAsync(
                asset: "BTC",
                currentPrice: currentBtcPrice,
                targetPrice: stopLossPrice,
                timeHorizonSeconds: timeHorizonHours * 3600,
                isLongPosition: true, // For SL in long position, we check downward movement
                config: config);

            Console.WriteLine($"🛑 Stop Loss Analysis (1% loss):");
            Console.WriteLine($"Current Price: ${currentBtcPrice:N0}");
            Console.WriteLine($"Stop Loss Price: ${stopLossPrice:N0}");
            Console.WriteLine($"Liquidation Risk: {stopLossProbability:P2}");

            Assert.True(stopLossProbability >= 0m && stopLossProbability <= 1m,
                "Stop loss probability should be between 0 and 1");

            // Risk assessment - typical risk thresholds
            if (stopLossProbability > 0.20m)
            {
                Console.WriteLine("⚠️ HIGH RISK: Liquidation probability exceeds 20%");
            }
            else if (stopLossProbability > 0.10m)
            {
                Console.WriteLine("⚡ MODERATE RISK: Liquidation probability between 10-20%");
            }
            else
            {
                Console.WriteLine("✅ LOW RISK: Liquidation probability below 10%");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"❌ Stop Loss test failed: {ex.Message}");
            Console.WriteLine("⚠️ Skipping Stop Loss test due to API issue");
        }

        Console.WriteLine($"\n📊 Money Management Summary:");
        Console.WriteLine($"Position: LONG BTC");
        Console.WriteLine($"Entry: ${currentBtcPrice:N0}");
        Console.WriteLine($"Take Profit: ${takeProfitPrice:N0} (+2.00%)");
        Console.WriteLine($"Stop Loss: ${stopLossPrice:N0} (-1.00%)");
        Console.WriteLine($"Risk/Reward Ratio: 1:2");
        Console.WriteLine($"Time Horizon: {timeHorizonHours} hours");
        Console.WriteLine("🏁 Test completed!");
    }


    [Fact]
    public async Task ValidateSignalAsync_ShouldUseCustomThresholds_ForSignalFiltering_RealAPI()
    {
        // Arrange - Static values for custom threshold testing
        const decimal currentBtcPrice = 107300m; // Current BTC price at $105,700

        Console.WriteLine($"🔧 Starting RiskManagement Configuration Test for BTC at ${currentBtcPrice:N0}");

        // Create real API client and service
        var httpClient = new HttpClient();
        var logger = new TestLogger<SynthPredictionService>();
        var synthApiClient = new SynthApiClient(httpClient, new TestLogger<SynthApiClient>());
        var mockSynthRepository = new Mock<ISynthRepository>();
        var synthPredictionService = new SynthPredictionService(synthApiClient, mockSynthRepository.Object, logger);

        // Define test scenarios for both LONG and SHORT signals
        var signalDirections = new[]
        {
            new { Direction = TradeDirection.Long, Name = "LONG" },
            new { Direction = TradeDirection.Short, Name = "SHORT" }
        };

        // Define RiskManagement configurations to test
        var riskConfigs = new[]
        {
            new
            {
                Name = "Default (Moderate)",
                RiskConfig = new RiskManagement
                {
                    AdverseProbabilityThreshold = 0.25m, // 25% - balanced threshold
                    FavorableProbabilityThreshold = 0.30m, // 30% - reasonable expectation
                    RiskAversion = 1.5m, // Moderate risk aversion
                    KellyMinimumThreshold = 0.02m, // 2% - practical minimum
                    KellyMaximumCap = 0.20m, // 20% - reasonable maximum
                    KellyFractionalMultiplier = 0.75m, // 75% of Kelly (conservative)
                    RiskTolerance = RiskToleranceLevel.Moderate
                }
            },
            new
            {
                Name = "Conservative",
                RiskConfig = new RiskManagement
                {
                    AdverseProbabilityThreshold = 0.20m, // 20% - stricter threshold
                    FavorableProbabilityThreshold = 0.40m, // 40% - higher TP expectation
                    RiskAversion = 2.0m, // Higher risk aversion
                    KellyMinimumThreshold = 0.03m, // 3% - higher minimum
                    KellyMaximumCap = 0.15m, // 15% - lower maximum
                    KellyFractionalMultiplier = 0.50m, // 50% of Kelly (very conservative)
                    RiskTolerance = RiskToleranceLevel.Conservative
                }
            },
            new
            {
                Name = "Aggressive",
                RiskConfig = new RiskManagement
                {
                    AdverseProbabilityThreshold = 0.35m, // 35% - more permissive
                    FavorableProbabilityThreshold = 0.25m, // 25% - lower TP barrier
                    RiskAversion = 1.0m, // Lower risk aversion
                    KellyMinimumThreshold = 0.01m, // 1% - lower minimum
                    KellyMaximumCap = 0.30m, // 30% - higher maximum
                    KellyFractionalMultiplier = 1.0m, // 100% of Kelly (full Kelly)
                    RiskTolerance = RiskToleranceLevel.Aggressive
                }
            },
            new
            {
                Name = "Moderate-Plus",
                RiskConfig = new RiskManagement
                {
                    AdverseProbabilityThreshold = 0.30m, // 30% - slightly more permissive
                    FavorableProbabilityThreshold = 0.35m, // 35% - balanced expectation
                    RiskAversion = 1.2m, // Slightly less risk-averse
                    KellyMinimumThreshold = 0.015m, // 1.5% - practical minimum
                    KellyMaximumCap = 0.25m, // 25% - reasonable maximum
                    KellyFractionalMultiplier = 0.85m, // 85% of Kelly
                    RiskTolerance = RiskToleranceLevel.Moderate
                }
            },
            new
            {
                Name = "Risk-Focused",
                RiskConfig = new RiskManagement
                {
                    AdverseProbabilityThreshold = 0.18m, // 18% - tight risk control
                    FavorableProbabilityThreshold = 0.45m, // 45% - high TP requirement
                    RiskAversion = 2.5m, // High risk aversion
                    KellyMinimumThreshold = 0.025m, // 2.5% - higher minimum
                    KellyMaximumCap = 0.12m, // 12% - very conservative maximum
                    KellyFractionalMultiplier = 0.40m, // 40% of Kelly (very conservative)
                    RiskTolerance = RiskToleranceLevel.Conservative
                }
            },
            new
            {
                Name = "Ultra-Conservative",
                RiskConfig = new RiskManagement
                {
                    AdverseProbabilityThreshold = 0.16m, // 16% - very strict threshold (should trigger some LOWs)
                    FavorableProbabilityThreshold = 0.60m, // 60% - very high TP requirement
                    RiskAversion = 3.5m, // Very high risk aversion
                    KellyMinimumThreshold = 0.04m, // 4% - high minimum barrier
                    KellyMaximumCap = 0.08m, // 8% - very low maximum (forces heavy capping)
                    KellyFractionalMultiplier = 0.25m, // 25% of Kelly (ultra conservative)
                    RiskTolerance = RiskToleranceLevel.Conservative
                }
            },
            new
            {
                Name =
                    "Paranoid-Blocking",
                RiskConfig = new RiskManagement
                {
                    AdverseProbabilityThreshold = 0.12m, // 12% - very strict (should block 22-25% SL signals)
                    FavorableProbabilityThreshold = 0.60m, // 60% - very high TP requirement
                    RiskAversion = 4.0m, // Extremely high risk aversion
                    KellyMinimumThreshold = 0.05m, // 5% - very high minimum
                    KellyMaximumCap = 0.06m, // 6% - extremely conservative maximum
                    KellyFractionalMultiplier = 0.15m, // 15% of Kelly (extremely conservative)
                    RiskTolerance = RiskToleranceLevel.Conservative,
                    SignalValidationTimeHorizonHours = 24
                }
            },
            new
            {
                Name = "Extreme-Blocking",
                RiskConfig = new RiskManagement
                {
                    AdverseProbabilityThreshold = 0.08m, // 8% - extremely strict (will block 22-25% SL signals)
                    FavorableProbabilityThreshold = 0.70m, // 70% - extremely high TP requirement
                    RiskAversion = 5.0m, // Maximum risk aversion
                    KellyMinimumThreshold = 0.08m, // 8% - very high minimum
                    KellyMaximumCap = 0.05m, // 5% - extremely small maximum
                    KellyFractionalMultiplier = 0.10m, // 10% of Kelly (ultra-conservative)
                    RiskTolerance = RiskToleranceLevel.Conservative,
                    SignalValidationTimeHorizonHours = 24
                }
            }
        };

        // Store results for summary
        var testResults =
            new Dictionary<string, Dictionary<string, SignalValidationResult>>();

        // Test each RiskManagement configuration with both LONG and SHORT signals
        foreach (var configTest in riskConfigs)
        {
            Console.WriteLine($"\n📊 Testing {configTest.Name})");
            testResults[configTest.Name] =
                new Dictionary<string, SignalValidationResult>();

            // Create bot configuration with the specific RiskManagement
            var botConfig = new TradingBotConfig
            {
                BotTradingBalance = 50000m, // $50k trading balance for realistic utility calculations
                Timeframe = Timeframe.FifteenMinutes,
                UseSynthApi = true,
                UseForSignalFiltering = true,
                UseForPositionSizing = true,
                UseForDynamicStopLoss = false,
                RiskManagement = configTest.RiskConfig, // Use the specific risk configuration
                MoneyManagement = new MoneyManagement
                {
                    Name = "Test Money Management",
                    StopLoss = 0.02m, // 2% stop loss
                    TakeProfit = 0.022m, // 4% take profit (1:2 risk/reward ratio)
                    Leverage = 10m,
                    Timeframe = Timeframe.FifteenMinutes
                }
            };

            foreach (var signal in signalDirections)
            {
                try
                {
                    Console.WriteLine($"  🎯 {signal.Name} Signal Test");

                    // Create a test signal for this direction
                    var testSignal = CreateTestSignal(Ticker.BTC, signal.Direction, currentBtcPrice);

                    var result = await synthPredictionService.ValidateSignalAsync(
                        signal: testSignal,
                        currentPrice: currentBtcPrice,
                        botConfig: botConfig,
                        isBacktest: false,
                        customThresholds: null); // No custom thresholds - use RiskManagement config

                    testResults[configTest.Name][signal.Name] = result;

                    Console.WriteLine($"    🎯 Confidence: {result.Confidence}");
                    Console.WriteLine(
                        $"    📊 SL Risk: {result.StopLossProbability:P2} | TP Prob: {result.TakeProfitProbability:P2}");
                    Console.WriteLine(
                        $"    🎲 TP/SL Ratio: {result.TpSlRatio:F2}x | Win/Loss: {result.WinLossRatio:F2}:1");
                    Console.WriteLine($"    💰 Expected Value: ${result.ExpectedMonetaryValue:F2}");
                    Console.WriteLine($"    🧮 Expected Utility: {result.ExpectedUtility:F4}");
                    Console.WriteLine(
                        $"    🎯 Kelly: {result.KellyFraction:P2} (Capped: {result.KellyCappedFraction:P2})");
                    Console.WriteLine($"    📊 Kelly Assessment: {result.KellyAssessment}");
                    Console.WriteLine($"    ✅ Kelly Favorable: {result.IsKellyFavorable(configTest.RiskConfig)}");
                    Console.WriteLine($"    🚫 Blocked: {result.IsBlocked}");

                    // Debug: Show actual probability values and threshold comparison
                    var adverseThreshold = configTest.RiskConfig.AdverseProbabilityThreshold;
                    Console.WriteLine(
                        $"    🔍 DEBUG - SL: {result.StopLossProbability:F4} | TP: {result.TakeProfitProbability:F4} | Threshold: {adverseThreshold:F4}");
                    Console.WriteLine(
                        $"    🔍 DEBUG - SL > Threshold: {result.StopLossProbability > adverseThreshold} | TP > SL: {result.TakeProfitProbability > result.StopLossProbability}");

                    // Assert that the method works with RiskManagement configuration
                    Assert.True(Enum.IsDefined(typeof(Confidence), result.Confidence),
                        $"{configTest.Name} - {signal.Name} signal should return a valid Confidence level");

                    // Assert that Kelly calculations were performed
                    Assert.True(result.KellyFraction >= 0, "Kelly fraction should be non-negative");
                    Assert.True(result.KellyCappedFraction >= 0, "Capped Kelly fraction should be non-negative");

                    // Assert that Expected Utility calculations were performed
                    Assert.True(result.TradingBalance > 0, "Trading balance should be set from bot config");
                    Assert.Equal(botConfig.BotTradingBalance, result.TradingBalance);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"    ❌ {signal.Name} signal test failed: {ex.Message}");
                    // Create a fallback result for error cases
                    testResults[configTest.Name][signal.Name] = new SignalValidationResult
                    {
                        Confidence = Confidence.High, // Default to high confidence on error
                        ValidationContext = $"Error: {ex.Message}"
                    };
                }
            }
        }

        // Display comprehensive results summary
        Console.WriteLine($"\n📈 Comprehensive RiskManagement Configuration Test Summary:");
        Console.WriteLine($"Asset: BTC | Price: ${currentBtcPrice:N0} | Trading Balance: ${50000:N0}");
        Console.WriteLine($"Stop Loss: 2.0% | Take Profit: 4.0% | Risk/Reward Ratio: 1:2.0");

        _testOutputHelper.WriteLine($"\n🎯 Results Matrix:");
        _testOutputHelper.WriteLine(
            $"{"Configuration",-20} {"LONG Confidence",-15} {"LONG Kelly",-12} {"SHORT Confidence",-16} {"SHORT Kelly",-12}");
        _testOutputHelper.WriteLine(new string('-', 85));

        foreach (var configTest in riskConfigs)
        {
            var longResult = testResults[configTest.Name].GetValueOrDefault("LONG");
            var shortResult = testResults[configTest.Name].GetValueOrDefault("SHORT");

            var longConf = longResult?.Confidence ?? Confidence.None;
            var shortConf = shortResult?.Confidence ?? Confidence.None;
            var longKelly = longResult?.KellyCappedFraction ?? 0m;
            var shortKelly = shortResult?.KellyCappedFraction ?? 0m;

            _testOutputHelper.WriteLine(
                $"{configTest.Name,-20} {GetConfidenceDisplay(longConf),-15} {longKelly,-12:P1} {GetConfidenceDisplay(shortConf),-16} {shortKelly,-12:P1}");
        }

        // Display detailed ValidationContext for each configuration and direction
        Console.WriteLine($"\n📊 Detailed Analysis Results:");
        Console.WriteLine(new string('=', 120));

        foreach (var configTest in riskConfigs)
        {
            Console.WriteLine($"\n🔧 {configTest.Name}");
            Console.WriteLine(new string('-', 80));

            var longResult = testResults[configTest.Name].GetValueOrDefault("LONG");
            var shortResult = testResults[configTest.Name].GetValueOrDefault("SHORT");

            if (longResult != null)
            {
                Console.WriteLine($"📈 LONG Signal Analysis:");
                Console.WriteLine($"   Context: {longResult.ValidationContext ?? "N/A"}");
                Console.WriteLine(
                    $"   Confidence: {GetConfidenceDisplay(longResult.Confidence)} | Blocked: {longResult.IsBlocked}");
                Console.WriteLine(
                    $"   Kelly Assessment: {longResult.KellyAssessment} | Kelly Favorable: {longResult.IsKellyFavorable(configTest.RiskConfig)}");
                if (longResult.TradingBalance > 0)
                {
                    Console.WriteLine(
                        $"   Trading Balance: ${longResult.TradingBalance:N0} | Risk Assessment: {longResult.GetUtilityRiskAssessment()}");
                }
            }
            else
            {
                Console.WriteLine($"📈 LONG Signal Analysis: ERROR - No result available");
            }

            Console.WriteLine(); // Empty line for separation

            if (shortResult != null)
            {
                Console.WriteLine($"📉 SHORT Signal Analysis:");
                Console.WriteLine($"   Context: {shortResult.ValidationContext ?? "N/A"}");
                Console.WriteLine(
                    $"   Confidence: {GetConfidenceDisplay(shortResult.Confidence)} | Blocked: {shortResult.IsBlocked}");
                Console.WriteLine(
                    $"   Kelly Assessment: {shortResult.KellyAssessment} | Kelly Favorable: {shortResult.IsKellyFavorable(configTest.RiskConfig)}");
                if (shortResult.TradingBalance > 0)
                {
                    Console.WriteLine(
                        $"   Trading Balance: ${shortResult.TradingBalance:N0} | Risk Assessment: {shortResult.GetUtilityRiskAssessment()}");
                }
            }
            else
            {
                Console.WriteLine($"📉 SHORT Signal Analysis: ERROR - No result available");
            }
        }

        Console.WriteLine($"\n📊 Risk Configuration Analysis:");
        Console.WriteLine($"• Default: Balanced 20% adverse threshold, 1% Kelly minimum");
        Console.WriteLine($"• Conservative: Strict 15% adverse, 2% Kelly min, half-Kelly multiplier");
        Console.WriteLine($"• Aggressive: Permissive 30% adverse, 0.5% Kelly min, full Kelly");
        Console.WriteLine($"• Custom Permissive: Very permissive 35% adverse, low barriers");
        Console.WriteLine($"• Custom Strict: Very strict 10% adverse, high barriers, conservative sizing");

        Console.WriteLine($"\n💡 Key Insights:");
        Console.WriteLine($"• Conservative configs should block more signals (lower confidence)");
        Console.WriteLine($"• Aggressive configs should allow more signals (higher confidence)");
        Console.WriteLine($"• Kelly fractions should vary based on risk tolerance settings");
        Console.WriteLine($"• Expected Utility should reflect trading balance and risk aversion");

        // Verify that we have results for all configurations and directions
        foreach (var configTest in riskConfigs)
        {
            foreach (var signal in signalDirections)
            {
                Assert.True(testResults.ContainsKey(configTest.Name) &&
                            testResults[configTest.Name].ContainsKey(signal.Name),
                    $"Should have test result for {configTest.Name} - {signal.Name}");

                var result = testResults[configTest.Name][signal.Name];
                Assert.NotNull(result);
                Assert.True(result.TradingBalance > 0, "Trading balance should be populated from bot config");
            }
        }

        Console.WriteLine("🏁 Comprehensive RiskManagement Configuration Test completed!");
    }

    /// <summary>
    /// Helper method to display confidence levels with emojis
    /// </summary>
    /// <param name="confidence">Confidence level</param>
    /// <returns>Formatted confidence display</returns>
    private static string GetConfidenceDisplay(Confidence confidence)
    {
        return confidence switch
        {
            Confidence.High => "🟢 HIGH",
            Confidence.Medium => "🟡 MEDIUM",
            Confidence.Low => "🟠 LOW",
            Confidence.None => "🔴 NONE",
            _ => "❓ UNKNOWN"
        };
    }
}

// Simple test logger implementation
public class TestLogger<T> : ILogger<T>
{
    public IDisposable BeginScope<TState>(TState state) => null;
    public bool IsEnabled(LogLevel logLevel) => true;

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
        Func<TState, Exception, string> formatter)
    {
        // Silent logger for tests - output goes to Console.WriteLine
    }
}