using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
using Predictalytics.Application.Interfaces;
using Predictalytics.Application.Services;
using Predictalytics.Domain.Entities;
using Predictalytics.Domain.Enums;
using Predictalytics.Domain.Interfaces;
using Predictalytics.Infrastructure.Data;
using Predictalytics.Infrastructure.Data.Repositories;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Predictalytics.Application.Tests.Services;

public class AnalyticsServiceTests
{
    private AppDbContext CreateDbContext()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;
        return new AppDbContext(options);
    }

    [Fact]
    public async Task GetTraderDeepDiveAsync_CalculatesCorrectMetrics()
    {
        // Arrange
        using var db = CreateDbContext();
        var traderRepo = new TraderRepository(db);
        var tradeRepo = new TradeRepository(db, Microsoft.Extensions.Logging.Abstractions.NullLogger<TradeRepository>.Instance);
        var watchlistRepo = new WatchlistRepository(db);

        var discoveryMock = new Mock<IDiscoveryService>();
        var pnlEngineMock = new Mock<IPositionPnLEngine>();
        var alertRepoMock = new Mock<IAlertRepository>();
        var watchlistRepoMock = new Mock<IWatchlistRepository>();
        var marketRepo = new MarketRepository(db);
        var providers = new List<IPlatformProvider>();

        var analyticsService = new AnalyticsService(
            traderRepo,
            tradeRepo,
            alertRepoMock.Object,
            watchlistRepoMock.Object,
            marketRepo,
            discoveryMock.Object,
            providers,
            pnlEngineMock.Object,
            NullLogger<AnalyticsService>.Instance
        );

        var trader = new Trader { Id = 1, PlatformUserId = "0x1", DisplayName = "Trader 1" };
        db.Traders.Add(trader);

        var market = new Market { Id = 10, PlatformMarketId = 1L, Question = "Q?" };
        var outcome = new MarketOutcome { Id = 100, MarketId = 10, Label = "Yes", TokenId = "t100", CurrentPrice = 0.50m };
        market.Outcomes.Add(outcome);
        db.Markets.Add(market);

        var baseTime = DateTime.UtcNow.AddDays(-5);
        db.Trades.Add(new Trade
        {
            Id = 501, TraderId = 1, DbMarketId = 10, MarketOutcomeId = 100,
            Side = TradeSide.Buy, Price = 0.40m, Size = 100m, Amount = 40m,
            ExecutedAt = baseTime
        });

        db.Trades.Add(new Trade
        {
            Id = 502, TraderId = 1, DbMarketId = 10, MarketOutcomeId = 100,
            Side = TradeSide.Sell, Price = 0.60m, Size = 100m, Amount = 60m,
            ExecutedAt = baseTime.AddHours(24) // 24 hours holding duration
        });

        db.MarketOutcomePriceSnapshots.Add(new MarketOutcomePriceSnapshot
        {
            Id = 1, MarketOutcomeId = 100, Price = 0.50m, Timestamp = baseTime.AddHours(2)
        });
        db.MarketOutcomePriceSnapshots.Add(new MarketOutcomePriceSnapshot
        {
            Id = 2, MarketOutcomeId = 100, Price = 0.60m, Timestamp = baseTime.AddHours(12)
        });

        await db.SaveChangesAsync();

        // Act
        var deepDive = await analyticsService.GetTraderDeepDiveAsync(1);

        // Assert
        Assert.NotNull(deepDive);
        Assert.Equal(24.0, (double)deepDive.AvgHoldDurationHours, 2);
        
        // Entry Quality: Buy at 0.40, subsequent prices are 0.50 and 0.60 (avg 0.55).
        // Entry Quality = 50 + ((0.55 - 0.40) / 0.40) * 100 = 50 + 0.375 * 100 = 87.5
        Assert.Equal(87.5m, deepDive.EntryQuality);
    }

    private class MockDiscoveryService : IDiscoveryService
    {
        public Task<int> ImportTraderAsync(PlatformType platform, string platformUserId, string displayName, bool isAutoDiscovered = true, CancellationToken ct = default)
        {
            return Task.FromResult(1);
        }
        public Task ScanTopHoldersAsync(CancellationToken ct) => Task.CompletedTask;
        public Task<IReadOnlyList<DiscoveredTrader>> RunDiscoveryAsync(PlatformType platform, CancellationToken ct = default)
        {
            return Task.FromResult<IReadOnlyList<DiscoveredTrader>>(new List<DiscoveredTrader>());
        }
    }
}
