using CartWise.Domain.Enums;

namespace CartWise.Domain.Entities;

/// <summary>
/// Append-only price history. There are intentionally no mutation methods on this type —
/// a new observation is always a new row, never an update to an existing one.
/// </summary>
public class PriceObservation
{
    public Guid PriceObservationId { get; private set; }

    public Guid? HouseholdId { get; private set; }

    public Guid ProductId { get; private set; }

    public Guid? StoreLocationId { get; private set; }

    public decimal Price { get; private set; }

    public decimal? SalePrice { get; private set; }

    public decimal? UnitPrice { get; private set; }

    public string CurrencyCode { get; private set; } = null!;

    public DateTime ObservedUtc { get; private set; }

    public PriceObservationSourceType SourceType { get; private set; }

    public string? SourceReference { get; private set; }

    public decimal ConfidenceScore { get; private set; }

    public DateTime CreatedUtc { get; private set; }

    private PriceObservation()
    {
    }

    public PriceObservation(
        Guid productId,
        decimal price,
        DateTime observedUtc,
        PriceObservationSourceType sourceType,
        decimal confidenceScore,
        DateTime createdUtc,
        Guid? householdId = null,
        Guid? storeLocationId = null,
        decimal? salePrice = null,
        decimal? unitPrice = null,
        string currencyCode = "USD",
        string? sourceReference = null)
    {
        if (productId == Guid.Empty)
        {
            throw new ArgumentException("ProductId is required.", nameof(productId));
        }

        if (price < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(price), "Price cannot be negative.");
        }

        if (confidenceScore is < 0 or > 1)
        {
            throw new ArgumentOutOfRangeException(nameof(confidenceScore), "ConfidenceScore must be between 0 and 1.");
        }

        if (string.IsNullOrWhiteSpace(currencyCode) || currencyCode.Length != 3)
        {
            throw new ArgumentException("CurrencyCode must be a 3-letter code.", nameof(currencyCode));
        }

        PriceObservationId = Guid.NewGuid();
        ProductId = productId;
        Price = price;
        ObservedUtc = observedUtc;
        SourceType = sourceType;
        ConfidenceScore = confidenceScore;
        HouseholdId = householdId;
        StoreLocationId = storeLocationId;
        SalePrice = salePrice;
        UnitPrice = unitPrice;
        CurrencyCode = currencyCode.Trim().ToUpperInvariant();
        SourceReference = sourceReference;
        CreatedUtc = createdUtc;
    }
}
