using CartWise.Domain.Entities;
using CartWise.Domain.Enums;
using System.Reflection;

namespace CartWise.Domain.Tests;

public class PriceObservationTests
{
    private static readonly DateTime Now = new(2026, 8, 10, 12, 0, 0, DateTimeKind.Utc);

    [Fact]
    public void Constructor_SetsDefaultCurrencyCode()
    {
        var observation = new PriceObservation(Guid.NewGuid(), 3.99m, Now, PriceObservationSourceType.UserEntered, 1m, Now);

        Assert.Equal("USD", observation.CurrencyCode);
    }

    [Fact]
    public void Constructor_ThrowsWhenProductIdIsEmpty()
    {
        Assert.Throws<ArgumentException>(() => new PriceObservation(Guid.Empty, 3.99m, Now, PriceObservationSourceType.UserEntered, 1m, Now));
    }

    [Fact]
    public void Constructor_ThrowsWhenPriceIsNegative()
    {
        Assert.Throws<ArgumentOutOfRangeException>(() => new PriceObservation(Guid.NewGuid(), -1m, Now, PriceObservationSourceType.UserEntered, 1m, Now));
    }

    [Theory]
    [InlineData(-0.01)]
    [InlineData(1.01)]
    public void Constructor_ThrowsWhenConfidenceScoreIsOutOfRange(decimal confidence)
    {
        Assert.Throws<ArgumentOutOfRangeException>(() => new PriceObservation(Guid.NewGuid(), 3.99m, Now, PriceObservationSourceType.UserEntered, confidence, Now));
    }

    [Fact]
    public void Constructor_ThrowsWhenCurrencyCodeIsNotThreeLetters()
    {
        Assert.Throws<ArgumentException>(() => new PriceObservation(Guid.NewGuid(), 3.99m, Now, PriceObservationSourceType.UserEntered, 1m, Now, currencyCode: "US"));
    }

    [Fact]
    public void Type_HasNoPublicMutationMethods()
    {
        // Append-only enforcement: nothing beyond the constructor may change an existing
        // observation's state. If this test ever needs updating, the append-only invariant
        // has likely been broken.
        var publicMethods = typeof(PriceObservation)
            .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
            .Where(m => !m.IsSpecialName) // exclude property getters
            .ToList();

        Assert.Empty(publicMethods);
    }
}
