using Xunit;
using KArtSell.BuildingBlocks.Time;
using KArtSell.Modules.ModelOperations.ShadowRun;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace KArtSell.Integration.Tests;

/// <summary>
/// Phase 1-2 로컬 실행 (Host 미필요, 즉시 결과)
/// - 252 거래일 시뮬레이션
/// - 메트릭 계산
/// - 게이트 검증
/// - 결과 요약
/// </summary>
public sealed class Phase1Phase2LocalExecutionTests
{
    private readonly ILogger<ReplayEngine> _replayLogger = new NoOpLogger<ReplayEngine>();
    private readonly ILogger<MetricsCalculator> _metricsLogger = new NoOpLogger<MetricsCalculator>();

    /// <summary>
    /// 252 거래일 실제 시뮬레이션 실행 (개선된 모델)
    /// Phase 1 + Phase 2 통합 테스트
    /// </summary>
    [Fact]
    public async Task ExecutePhase1AndPhase2_FullYearSimulation()
    {
        // Arrange: 252일 실제 데이터 생성
        var startDate = new DateOnly(2025, 8, 12);
        var endDate = new DateOnly(2026, 8, 12);
        var bars = Generate252TradingDaysData(startDate, endDate);
        var sessions = bars.Select(b => b.Date).Distinct().OrderBy(d => d).ToList();

        var fees = new List<DataBackfiller.FeeScheduleEntry>
        {
            new(new DateOnly(2025, 8, 1), 0.001m, 0.0005m),
        };

        var initialCapital = 10_000_000m;

        // Act: Phase 1 - ReplayAsync (252 거래일)
        var startTime = DateTime.UtcNow;

        var replay = new ReplayEngine(_replayLogger);
        var result = await replay.ReplayAsync(
            Guid.NewGuid(),
            bars,
            fees,
            initialCapital,
            sessions,
            CancellationToken.None);

        var phase1Duration = DateTime.UtcNow - startTime;

        // Act: Phase 2 - Metrics (자동 계산)
        var calculator = new MetricsCalculator(_metricsLogger);
        var metrics = await calculator.CalculateAsync(
            result,
            bars,
            fees,
            CancellationToken.None);

        // Assert & Report
        Assert.NotNull(result);
        Assert.NotNull(metrics);

        // Phase 1 검증
        Assert.True(result.DailyReturns.Count > 0, "Should have daily returns");
        Assert.True(result.Signals.Count > 0, "Should have signals from EMA");
        Assert.True(result.Orders.Count > 0, "Should have orders from signals");

        // Phase 2 게이트 검증
        var pboPass = metrics.ProbOfBacktestOverfit <= 0.20m;
        var dsrPass = metrics.DailySharePercentile >= 0.95m;
        var costPass = metrics.TotalReturn > 0m;

        var allGatesPassed = pboPass && dsrPass && costPass;

        // 결과 출력
        var separator = new string('=', 70);
        Console.WriteLine("\n" + separator);
        Console.WriteLine("🎯 PHASE 1-2 로컬 실행 완료");
        Console.WriteLine(separator);

        Console.WriteLine($"\n📊 Phase 1 결과 (252 거래일):");
        Console.WriteLine($"   실행 시간: {phase1Duration.TotalSeconds:F2} 초");
        Console.WriteLine($"   거래일: {sessions.Count}");
        Console.WriteLine($"   신호 생성: {result.Signals.Count}");
        Console.WriteLine($"   주문 체결: {result.Orders.Count}");
        Console.WriteLine($"   포트폴리오 스냅샷: {result.PortfolioHistory.Count}");

        var finalValue = result.PortfolioHistory[result.PortfolioHistory.Count - 1].TotalValue;
        var totalReturn = (finalValue - initialCapital) / initialCapital;

        Console.WriteLine($"\n💰 P&L:");
        Console.WriteLine($"   초기 자본: ${initialCapital:N0}");
        Console.WriteLine($"   최종 가치: ${finalValue:N0}");
        Console.WriteLine($"   총 수익률: {(totalReturn * 100):F2}%");

        Console.WriteLine($"\n📈 Phase 2 메트릭:");
        Console.WriteLine($"   Total Return: {(metrics.TotalReturn * 100):F2}%");
        Console.WriteLine($"   Sharpe Ratio: {metrics.SharpeRatio:F4}");
        Console.WriteLine($"   PBO (Prob of Backtest Overfit): {(metrics.ProbOfBacktestOverfit * 100):F2}%");
        Console.WriteLine($"   DSR (Daily Sharpe Percentile): {(metrics.DailySharePercentile * 100):F2}%");

        Console.WriteLine($"\n🎯 Phase 2 게이트 검증:");
        Console.WriteLine($"   Gate 1 (PBO ≤ 20%): {(pboPass ? "✅" : "❌")} ({(metrics.ProbOfBacktestOverfit * 100):F1}%)");
        Console.WriteLine($"   Gate 2 (DSR ≥ 95%): {(dsrPass ? "✅" : "❌")} ({(metrics.DailySharePercentile * 100):F1}%)");
        Console.WriteLine($"   Gate 3 (Cost > 0): {(costPass ? "✅" : "❌")} ({(metrics.TotalReturn * 100):F1}%)");

        Console.WriteLine($"\n{(allGatesPassed ? "✅" : "⚠️")} AllGatesPassed: {allGatesPassed}");
        Console.WriteLine(new string('=', 70));

        Console.WriteLine($"\n📋 다음 단계:");
        if (allGatesPassed)
        {
            Console.WriteLine("   ✅ Phase 3 OOS 검증 준비 완료");
            Console.WriteLine("   → Hangfire 21:00 KST 자동 실행 시 바로 Phase 3 진행");
        }
        else
        {
            Console.WriteLine("   ⚠️ Phase 3 차단 (게이트 미통과)");
            Console.WriteLine($"   → 모델 추가 튜닝 필요");
            Console.WriteLine($"   → Gate 1: PBO {(metrics.ProbOfBacktestOverfit * 100):F1}% (need ≤20%)");
            Console.WriteLine($"   → Gate 2: DSR {(metrics.DailySharePercentile * 100):F1}% (need ≥95%)");
        }
    }

    private List<DataBackfiller.OhlcvBar> Generate252TradingDaysData(DateOnly start, DateOnly end)
    {
        var bars = new List<DataBackfiller.OhlcvBar>();
        var random = new Random(42);
        var basePrice = 2500m;
        var currentPrice = basePrice;

        int tradingDay = 0;
        for (int calendarDay = 0; calendarDay < 400 && tradingDay < 252; calendarDay++)
        {
            var date = start.AddDays(calendarDay);
            if (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)
                continue;
            if (date > end) break;

            // Realistic price: ±2% daily drift + trend
            var dailyReturn = (decimal)((random.NextDouble() - 0.5) * 0.04);
            var trend = (calendarDay % 252) < 126 ? 0.0001m : -0.00005m;
            currentPrice = currentPrice * (1m + dailyReturn + trend);
            currentPrice = Math.Max(2000m, currentPrice);

            bars.Add(new DataBackfiller.OhlcvBar(
                date, "KOSPI",
                currentPrice * 0.99m,
                currentPrice * 1.01m,
                currentPrice * 0.98m,
                currentPrice,
                1_000_000L));

            tradingDay++;
        }

        return bars;
    }

    private sealed class NoOpLogger<T> : ILogger<T>
    {
        public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
        public bool IsEnabled(LogLevel logLevel) => false;
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
            Func<TState, Exception?, string> formatter) { }
    }
}
