namespace Books.Api.Domain.Orders;

/// <summary>
/// Value object representing a single line on an order.
/// Contains product/service description, quantity, pricing, and VAT information.
/// Tracks whether the line has been invoiced.
/// </summary>
public sealed record OrderLine
{
    /// <summary>
    /// Line number for ordering (1-based).
    /// </summary>
    public int LineNumber { get; init; }

    /// <summary>
    /// Description of the product or service.
    /// </summary>
    public string Description { get; init; } = string.Empty;

    /// <summary>
    /// Quantity (can be decimal for services billed by hour).
    /// </summary>
    public decimal Quantity { get; init; }

    /// <summary>
    /// Unit of measurement (e.g., "stk", "timer", "kg").
    /// </summary>
    public string? Unit { get; init; }

    /// <summary>
    /// Price per unit excluding VAT.
    /// </summary>
    public decimal UnitPrice { get; init; }

    /// <summary>
    /// Discount percentage (0-100).
    /// </summary>
    public decimal DiscountPercent { get; init; }

    /// <summary>
    /// VAT code (e.g., "U25" for 25%, "UEU" for EU sales, "UEXP" for export).
    /// </summary>
    public string VatCode { get; init; } = "U25";

    /// <summary>
    /// Revenue account to credit when line is invoiced.
    /// If null, uses customer's default revenue account.
    /// </summary>
    public string? AccountId { get; init; }

    /// <summary>
    /// Product ID if this line was created from a product.
    /// </summary>
    public string? ProductId { get; init; }

    /// <summary>
    /// True if this line has been invoiced.
    /// </summary>
    public bool IsInvoiced { get; init; }

    /// <summary>
    /// The invoice ID where this line was invoiced.
    /// Null if not yet invoiced.
    /// </summary>
    public string? InvoiceId { get; init; }

    /// <summary>
    /// When this line was invoiced.
    /// </summary>
    public DateTimeOffset? InvoicedAt { get; init; }

    /// <summary>
    /// Calculated: UnitPrice * Quantity * (1 - DiscountPercent/100)
    /// </summary>
    public decimal AmountExVat => Math.Round(UnitPrice * Quantity * (1 - DiscountPercent / 100m), 2);

    /// <summary>
    /// Calculated VAT amount based on VatCode.
    /// </summary>
    public decimal AmountVat => Math.Round(AmountExVat * GetVatRate(), 2);

    /// <summary>
    /// Calculated: AmountExVat + AmountVat
    /// </summary>
    public decimal AmountTotal => AmountExVat + AmountVat;

    /// <summary>
    /// Gets the VAT rate for this line based on VatCode.
    /// </summary>
    private decimal GetVatRate() => VatCode switch
    {
        "U25" or "I25" => 0.25m,    // Danish standard 25%
        "UEU" or "IEU" => 0m,       // EU sales (reverse charge)
        "UEXP" or "IEXP" => 0m,     // Export (no VAT)
        "INGEN" => 0m,              // No VAT
        _ => 0.25m                   // Default to Danish standard
    };

    /// <summary>
    /// Creates an OrderLine with validation.
    /// </summary>
    public static OrderLine Create(
        int lineNumber,
        string description,
        decimal quantity,
        decimal unitPrice,
        string? vatCode = null,
        string? accountId = null,
        string? unit = null,
        decimal discountPercent = 0,
        string? productId = null)
    {
        if (lineNumber < 1)
            throw new ArgumentException("Line number must be at least 1", nameof(lineNumber));

        if (string.IsNullOrWhiteSpace(description))
            throw new ArgumentException("Description is required", nameof(description));

        if (quantity <= 0)
            throw new ArgumentException("Quantity must be positive", nameof(quantity));

        if (unitPrice < 0)
            throw new ArgumentException("Unit price cannot be negative", nameof(unitPrice));

        if (discountPercent < 0 || discountPercent > 100)
            throw new ArgumentException("Discount must be between 0 and 100", nameof(discountPercent));

        return new OrderLine
        {
            LineNumber = lineNumber,
            Description = description.Trim(),
            Quantity = quantity,
            UnitPrice = unitPrice,
            VatCode = vatCode ?? "U25",
            AccountId = accountId,
            Unit = unit,
            DiscountPercent = discountPercent,
            ProductId = productId,
            IsInvoiced = false,
            InvoiceId = null,
            InvoicedAt = null
        };
    }

    /// <summary>
    /// Returns a copy of this line marked as invoiced.
    /// </summary>
    public OrderLine MarkAsInvoiced(string invoiceId, DateTimeOffset invoicedAt)
    {
        return this with
        {
            IsInvoiced = true,
            InvoiceId = invoiceId,
            InvoicedAt = invoicedAt
        };
    }
}
