using CashLineERP.SharedKernel.Tax;

namespace CashLineERP.Domain.Sales;

public sealed class SalesOrderLine
{
    public long Id { get; private set; }

    public long SalesOrderId { get; private set; }

    public int LineNumber { get; private set; }

    public long ItemId { get; private set; }

    public long WarehouseId { get; private set; }

    public decimal OrderedQuantity { get; private set; }

    public decimal AllocatedQuantity { get; private set; }

    public decimal DeliveredQuantity { get; private set; }

    public string UnitOfMeasureCode { get; private set; } = string.Empty;

    public decimal UnitPrice { get; private set; }

    public decimal BaseUnitPrice { get; private set; }

    public long? PricingRuleId { get; private set; }

    public string? PricingRuleCode { get; private set; }

    public string PricingSourceCode { get; private set; } = "MANUAL";

    public string? TaxCode { get; private set; }

    public decimal DiscountAmount { get; private set; }

    public decimal DiscountPercent { get; private set; }

    public long? DiscountOverrideId { get; private set; }

    public string DiscountSourceCode { get; private set; } = "NONE";

    public decimal LineNetAmount { get; private set; }

    public decimal LineTaxAmount { get; private set; }

    public decimal LineTotalAmount { get; private set; }

    public SalesOrderLine(
        long salesOrderId,
        int lineNumber,
        long itemId,
        long warehouseId,
        decimal orderedQuantity,
        string unitOfMeasureCode,
        decimal unitPrice,
        decimal baseUnitPrice,
        long? pricingRuleId,
        string? pricingRuleCode,
        string pricingSourceCode,
        string? taxCode)
    {
        SalesOrderId = salesOrderId;
        LineNumber = lineNumber;
        ItemId = itemId;
        WarehouseId = warehouseId;
        OrderedQuantity = orderedQuantity;
        UnitOfMeasureCode = unitOfMeasureCode;
        UnitPrice = unitPrice;
        BaseUnitPrice = baseUnitPrice;
        PricingRuleId = pricingRuleId;
        PricingRuleCode = pricingRuleCode;
        PricingSourceCode = pricingSourceCode;
        TaxCode = taxCode;
        RecalculateAmounts();
    }

    private SalesOrderLine()
    {
    }

    public void Allocate(decimal quantity)
    {
        AllocatedQuantity += quantity;
    }

    public void RegisterDelivery(decimal quantity)
    {
        DeliveredQuantity += quantity;
    }

    public void ApplyApprovedDiscountOverride(long discountOverrideId, decimal approvedUnitPrice)
    {
        if (approvedUnitPrice <= 0m)
        {
            throw new InvalidOperationException("Approved discount override unit price must be greater than zero.");
        }

        if (approvedUnitPrice >= BaseUnitPrice)
        {
            throw new InvalidOperationException("Approved discount override must reduce the base unit price.");
        }

        if (DiscountOverrideId.HasValue)
        {
            throw new InvalidOperationException("Sales order line already has an approved discount override.");
        }

        UnitPrice = approvedUnitPrice;
        DiscountOverrideId = discountOverrideId;
        DiscountSourceCode = "OVERRIDE_APPROVED";
        RecalculateAmounts();
    }

    private void RecalculateAmounts()
    {
        var grossAmount = OrderedQuantity * BaseUnitPrice;
        LineNetAmount = OrderedQuantity * UnitPrice;
        DiscountAmount = grossAmount - LineNetAmount;
        DiscountPercent = grossAmount == 0m
            ? 0m
            : Math.Round((DiscountAmount / grossAmount) * 100m, 4, MidpointRounding.AwayFromZero);
        LineTaxAmount = StatutoryTaxPolicy.CalculateTax(LineNetAmount, TaxCode);
        LineTotalAmount = LineNetAmount + LineTaxAmount;
    }
}
