using Xunit;
using KArtSell.Modules.ModelOperations.ShadowRun.Services;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;

namespace KArtSell.Integration.Tests;

/// <summary>
/// Tests for market calendar: trading sessions, holiday exclusion, determinism.
/// </summary>
public sealed class MarketCalendarServiceTests : IAsyncLifetime
{
    private IMemoryCache _cache = null!;
    private ILogger<MarketCalendarService> _logger = null!;

    public Task InitializeAsync()
    {
        _cache = new MemoryCache(new MemoryCacheOptions());
        _logger = new NoOpLogger<MarketCalendarService>();
        return Task.CompletedTask;
    }

    public Task DisposeAsync()
    {
        _cache?.Dispose();
        return Task.CompletedTask;
    }

    [Fact]
    public async Task GetTradingSessionsAsync_ReturnsSessionsInWindow()
    {
        // Arrange
        var service = new MarketCalendarService(_cache, _logger);
        var startDate = new DateOnly(2024, 1, 2);
        var endDate = new DateOnly(2024, 1, 31);

        // Act
        var sessions = await service.GetTradingSessionsAsync(startDate, endDate, CancellationToken.None);

        // Assert
        Assert.NotEmpty(sessions);
        Assert.All(sessions, session =>
        {
            Assert.True(session >= startDate && session <= endDate);
            Assert.NotEqual(DayOfWeek.Saturday, session.DayOfWeek);
            Assert.NotEqual(DayOfWeek.Sunday, session.DayOfWeek);
        });
    }

    [Fact]
    public async Task GetTradingSessionsAsync_ExcludesHolidays()
    {
        // Arrange
        var service = new MarketCalendarService(_cache, _logger);
        var startDate = new DateOnly(2024, 2, 1);
        var endDate = new DateOnly(2024, 2, 15);  // Includes Lunar New Year

        // Act
        var sessions = await service.GetTradingSessionsAsync(startDate, endDate, CancellationToken.None);

        // Assert
        // 2024-02-09 (Lunar New Year Eve), 2024-02-10 (Lunar New Year), 2024-02-11, 2024-02-12 should be excluded
        var lunarNewYearDates = new[]
        {
            new DateOnly(2024, 2, 9),
            new DateOnly(2024, 2, 10),
            new DateOnly(2024, 2, 11),
            new DateOnly(2024, 2, 12)
        };

        Assert.DoesNotContain(lunarNewYearDates, d => sessions.Contains(d));
    }

    [Fact]
    public async Task GetTradingSessionsAsync_IsDeterministic()
    {
        // Arrange
        var service = new MarketCalendarService(_cache, _logger);
        var startDate = new DateOnly(2024, 1, 2);
        var endDate = new DateOnly(2024, 1, 31);

        // Act: Call twice
        var sessions1 = await service.GetTradingSessionsAsync(startDate, endDate, CancellationToken.None);
        var sessions2 = await service.GetTradingSessionsAsync(startDate, endDate, CancellationToken.None);

        // Assert
        Assert.Equal(sessions1.Count, sessions2.Count);
        for (int i = 0; i < sessions1.Count; i++)
        {
            Assert.Equal(sessions1[i], sessions2[i]);
        }
    }

    [Fact]
    public async Task GetTradingSessionsAsync_Covers252DaysForAnnualWindow()
    {
        // Arrange
        var service = new MarketCalendarService(_cache, _logger);
        var startDate = new DateOnly(2024, 1, 2);
        var endDate = new DateOnly(2025, 1, 1);

        // Act
        var sessions = await service.GetTradingSessionsAsync(startDate, endDate, CancellationToken.None);

        // Assert
        // Typical: 250–252 trading days per year (accounting for holidays)
        Assert.InRange(sessions.Count, 245, 260);
    }

    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) { }
    }
}
