using ERPCore.Domain.Entities;
using ERPCore.Domain.Enums;
using ERPCore.Services.Interfaces;
using ERPCore.System.Errors;

namespace ERPCore.Services.Stock;

/// <summary>
/// Content ↔ pack arithmetic (see <see cref="IItemMeasure"/>). Stateless and I/O-free:
/// the content size is already on the <see cref="Item"/> every caller has loaded.
/// </summary>
public sealed class ItemMeasure : IItemMeasure
{
    public bool HasContent(Item item) => item.ContentBaseQty is > 0m;

    public decimal ToPacks(Item item, decimal formulaQty, StageQtyUnit unit)
    {
        if (unit == StageQtyUnit.Pack) return formulaQty;

        // Rounded to the quantity columns' (18,4) scale with the same mode as
        // ProductionRunService.Scale, so what FIFO consumes and what the cost pool divides
        // by are the same number to the last stored digit.
        return Math.Round(formulaQty / RequireContent(item), 4, MidpointRounding.AwayFromZero);
    }

    public decimal FromPacks(Item item, decimal packs, StageQtyUnit unit)
    {
        if (unit == StageQtyUnit.Pack) return packs;

        return Math.Round(packs * RequireContent(item), 4, MidpointRounding.AwayFromZero);
    }

    private static decimal RequireContent(Item item)
        => item.ContentBaseQty is > 0m
            ? item.ContentBaseQty.Value
            : throw new DomainException(
                ErrorCodes.Validation,
                $"Item {item.ItemId} has no content size, so its quantity cannot be expressed in content units.", 422);
}
