using KArtSell.Modules.ModelOperations.Domain;
using Xunit;

namespace KArtSell.ModelOperations.UnitTests;

public class VS03_MarketDataPolicyTests
{
    [Fact]
    public void ValidatePrice_ValidPrice_ReturnsPass()
    {
        var price = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-1)),
            OpenPrice: 70000m,
            HighPrice: 71000m,
            LowPrice: 69000m,
            ClosePrice: 70500m,
            Volume: 1000000,
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var result = MarketDataPolicy.ValidatePrice(price, DateOnly.FromDateTime(DateTime.UtcNow));

        Assert.True(result.IsValid);
        Assert.Empty(result.Errors);
        Assert.True(result.QualityScore >= 85);
    }

    [Fact]
    public void ValidatePrice_NegativePrice_ReturnsFail()
    {
        var price = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-1)),
            OpenPrice: -100m,
            HighPrice: 71000m,
            LowPrice: 69000m,
            ClosePrice: 70500m,
            Volume: 1000000,
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var result = MarketDataPolicy.ValidatePrice(price, DateOnly.FromDateTime(DateTime.UtcNow));

        Assert.False(result.IsValid);
        Assert.Contains("Open price must be > 0", result.Errors);
    }

    [Fact]
    public void ValidatePrice_HighLowerThanLow_ReturnsFail()
    {
        var price = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-1)),
            OpenPrice: 70000m,
            HighPrice: 68000m,
            LowPrice: 69000m,
            ClosePrice: 70500m,
            Volume: 1000000,
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var result = MarketDataPolicy.ValidatePrice(price, DateOnly.FromDateTime(DateTime.UtcNow));

        Assert.False(result.IsValid);
        Assert.Contains("High must be >= Low", result.Errors);
    }

    [Fact]
    public void ValidatePrice_FutureDate_ReturnsFail()
    {
        var price = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(1)),
            OpenPrice: 70000m,
            HighPrice: 71000m,
            LowPrice: 69000m,
            ClosePrice: 70500m,
            Volume: 1000000,
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var result = MarketDataPolicy.ValidatePrice(price, DateOnly.FromDateTime(DateTime.UtcNow));

        Assert.False(result.IsValid);
        Assert.Contains("cannot be in the future", result.Errors[0]);
    }

    [Fact]
    public void ValidatePrice_ZeroVolume_LowersScore()
    {
        var price = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-1)),
            OpenPrice: 70000m,
            HighPrice: 71000m,
            LowPrice: 69000m,
            ClosePrice: 70500m,
            Volume: 0,
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var result = MarketDataPolicy.ValidatePrice(price, DateOnly.FromDateTime(DateTime.UtcNow));

        Assert.True(result.IsValid);
        Assert.True(result.QualityScore < 80); // Quality degraded but still valid
    }

    [Fact]
    public void IsDuplicate_IdenticalPrice_ReturnsTrue()
    {
        var price1 = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-1)),
            OpenPrice: 70000m,
            HighPrice: 71000m,
            LowPrice: 69000m,
            ClosePrice: 70500m,
            Volume: 1000000,
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var price2 = price1 with { PriceId = Guid.NewGuid(), Revision = 2 };

        var isDuplicate = MarketDataPolicy.IsDuplicate(price2, new List<DailyPrice> { price1 });

        Assert.True(isDuplicate);
    }

    [Fact]
    public void IsDuplicate_DifferentSymbol_ReturnsFalse()
    {
        var price1 = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-1)),
            OpenPrice: 70000m,
            HighPrice: 71000m,
            LowPrice: 69000m,
            ClosePrice: 70500m,
            Volume: 1000000,
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var price2 = price1 with
        {
            PriceId = Guid.NewGuid(),
            Symbol = "000660"
        };

        var isDuplicate = MarketDataPolicy.IsDuplicate(price2, new List<DailyPrice> { price1 });

        Assert.False(isDuplicate);
    }

    [Fact]
    public void NormalizePrice_ValidPrice_RoundsTo2Decimals()
    {
        var price = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-1)),
            OpenPrice: 70000.123m,
            HighPrice: 71000.456m,
            LowPrice: 69000.789m,
            ClosePrice: 70500.999m,
            Volume: 1000000,
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var normalized = MarketDataPolicy.NormalizePrice(price);

        Assert.NotNull(normalized);
        Assert.Equal(70000.12m, normalized!.OpenPrice);
        Assert.Equal(71000.46m, normalized.HighPrice);
    }

    [Fact]
    public void NormalizePrice_LowVolume_ReturnsNull()
    {
        var price = new DailyPrice(
            PriceId: Guid.NewGuid(),
            Symbol: "005930",
            TradingDate: DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-1)),
            OpenPrice: 70000m,
            HighPrice: 71000m,
            LowPrice: 69000m,
            ClosePrice: 70500m,
            Volume: 50, // Suspiciously low
            PublishedAt: DateTime.UtcNow,
            Revision: 1,
            DataSource: "KRX",
            CorrelationId: "test-123");

        var normalized = MarketDataPolicy.NormalizePrice(price);

        Assert.Null(normalized);
    }

    [Fact]
    public void ClassifyQualityIssue_HighScore_ReturnsAccept()
    {
        var result = new ValidationResult(IsValid: true, Errors: new(), QualityScore: 95);

        var decision = MarketDataPolicy.ClassifyQualityIssue(result);

        Assert.Equal(DataQualityDecision.Accept, decision);
    }

    [Fact]
    public void ClassifyQualityIssue_MediumScore_ReturnsAcceptWithWarning()
    {
        var result = new ValidationResult(IsValid: true, Errors: new(), QualityScore: 75);

        var decision = MarketDataPolicy.ClassifyQualityIssue(result);

        Assert.Equal(DataQualityDecision.AcceptWithWarning, decision);
    }

    [Fact]
    public void ClassifyQualityIssue_LowScore_ReturnsQuarantine()
    {
        var result = new ValidationResult(IsValid: true, Errors: new(), QualityScore: 60);

        var decision = MarketDataPolicy.ClassifyQualityIssue(result);

        Assert.Equal(DataQualityDecision.Quarantine, decision);
    }
}
