using ERPCore.Domain.Enums;
using ERPCore.System.Errors;

namespace ERPCore.Services;

/// <summary>
/// Item content-size rules: validate the (qty, unit) pair and normalise it to a base
/// unit. Pure — no DI, no database — because everything it needs is on the item row.
/// <para>
/// Litres and kilograms exist only at the point of entry. Everything stored and every
/// downstream calculation works in millilitres or grams, so no consumer ever has to ask
/// which unit it is holding.
/// </para>
/// </summary>
public static class ItemContent
{
    /// <summary>
    /// Rejects a half-filled pair. Both null is valid and means "this item has no
    /// measurable content" — a screw, a label, a service.
    /// </summary>
    public static void ValidatePair(decimal? contentQty, MeasureUnit? contentUnit)
    {
        if (contentQty is null && contentUnit is null) return;

        if (contentQty is null || contentUnit is null)
            throw new DomainException(
                ErrorCodes.Validation,
                "contentQty and contentUnit must be supplied together, or both left null.", 422);

        if (contentQty <= 0)
            throw new DomainException(
                ErrorCodes.Validation, "contentQty must be greater than zero.", 422);
    }

    /// <summary>
    /// Converts an entered content size to its base unit: L → Ml and Kg → G, both ×1000;
    /// Ml and G pass through. Both-null in, both-null out.
    /// <para>
    /// Rounded to 4dp AwayFromZero to match the quantity columns' <c>(18,4)</c> scale and
    /// <c>ProductionRunService.Scale</c>, so a content size can never carry precision the
    /// database would silently drop.
    /// </para>
    /// </summary>
    public static (decimal? BaseQty, MeasureUnit? BaseUnit) Normalize(
        decimal? contentQty, MeasureUnit? contentUnit)
    {
        if (contentQty is null || contentUnit is null) return (null, null);

        var (factor, baseUnit) = contentUnit.Value switch
        {
            MeasureUnit.Ml => (1m, MeasureUnit.Ml),
            MeasureUnit.L => (1000m, MeasureUnit.Ml),
            MeasureUnit.G => (1m, MeasureUnit.G),
            MeasureUnit.Kg => (1000m, MeasureUnit.G),
            _ => throw new DomainException(
                ErrorCodes.Validation, $"Unsupported content unit '{contentUnit}'.", 422)
        };

        return (Math.Round(contentQty.Value * factor, 4, MidpointRounding.AwayFromZero), baseUnit);
    }
}
