using CashLineERP.SharedKernel.Inventory;

namespace CashLineERP.Domain.Sales;

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

    public long DispatchOrderId { get; private set; }

    public int LineNumber { get; private set; }

    public long SalesOrderLineId { get; private set; }

    public long ItemId { get; private set; }

    public decimal QuantityDispatched { get; private set; }

    public decimal QuantityDelivered { get; private set; }

    public decimal QuantityFailed { get; private set; }

    public decimal QuantityRecovered { get; private set; }

    public long? WarehouseLocationId { get; private set; }

    public string? BatchNumber { get; private set; }

    public string? SerialNumber { get; private set; }

    public DispatchOrderLine(
        long dispatchOrderId,
        int lineNumber,
        long salesOrderLineId,
        long itemId,
        decimal quantityDispatched,
        long? warehouseLocationId,
        string? batchNumber,
        string? serialNumber)
    {
        LotSerialTrackingGuard.EnsureSerialQuantity(serialNumber, quantityDispatched, "Dispatch order line");
        DispatchOrderId = dispatchOrderId;
        LineNumber = lineNumber;
        SalesOrderLineId = salesOrderLineId;
        ItemId = itemId;
        QuantityDispatched = quantityDispatched;
        WarehouseLocationId = warehouseLocationId;
        BatchNumber = LotSerialTrackingGuard.NormalizeTrackingCode(batchNumber);
        SerialNumber = LotSerialTrackingGuard.NormalizeTrackingCode(serialNumber);
    }

    private DispatchOrderLine()
    {
    }

    public void MarkDelivered(decimal quantityDelivered)
    {
        LotSerialTrackingGuard.EnsureSerialDelivery(SerialNumber, quantityDelivered, "Dispatch delivery line");
        QuantityDelivered = quantityDelivered;
    }

    public void RecordFailedQuantity(decimal quantityFailed)
    {
        if (quantityFailed < 0m)
        {
            throw new InvalidOperationException("Failed delivery quantity cannot be negative.");
        }

        if (quantityFailed > QuantityDispatched)
        {
            throw new InvalidOperationException("Failed delivery quantity cannot exceed the dispatched quantity.");
        }

        var quantityDelivered = QuantityDispatched - quantityFailed;
        LotSerialTrackingGuard.EnsureSerialDelivery(SerialNumber, quantityDelivered, "Dispatch delivery line");

        QuantityFailed = quantityFailed;
        QuantityDelivered = quantityDelivered;
        QuantityRecovered = 0m;
    }

    public void RecoverFailedQuantity(decimal quantityRecovered)
    {
        if (quantityRecovered <= 0m)
        {
            throw new InvalidOperationException("Recovered quantity must be greater than zero.");
        }

        var remainingFailedQuantity = QuantityFailed - QuantityRecovered;
        if (quantityRecovered > remainingFailedQuantity)
        {
            throw new InvalidOperationException("Recovered quantity cannot exceed the remaining failed quantity.");
        }

        LotSerialTrackingGuard.EnsureSerialQuantity(SerialNumber, quantityRecovered, "Failed delivery recovery line");
        QuantityRecovered += quantityRecovered;
    }
}
