namespace Books.Api.Domain.Invoices;

/// <summary>
/// Invoice status representing the lifecycle of an invoice or credit note.
/// Invoice workflow: Draft → Sent → PartiallyPaid → Paid → (optionally Voided at any point)
/// Credit note workflow: Draft → Issued → PartiallyApplied → FullyApplied → (optionally Voided at any point)
/// </summary>
public enum InvoiceStatus
{
    /// <summary>
    /// Invoice/credit note is being prepared, not yet sent/issued to customer.
    /// No ledger entries have been created.
    /// </summary>
    Draft,

    /// <summary>
    /// Invoice has been sent to customer and posted to the ledger.
    /// Debits the customer's sub-ledger account (1900-XXXX).
    /// </summary>
    Sent,

    /// <summary>
    /// Credit note has been issued to customer and posted to the ledger.
    /// Credits the customer's sub-ledger account (1900-XXXX).
    /// Functionally equivalent to "Sent" for credit notes.
    /// </summary>
    Issued,

    /// <summary>
    /// Partial payment has been received (for invoices).
    /// Amount paid > 0 but &lt; total amount.
    /// </summary>
    PartiallyPaid,

    /// <summary>
    /// Partial credit has been applied (for credit notes).
    /// Amount applied > 0 but &lt; total credit amount.
    /// Functionally equivalent to "PartiallyPaid" for credit notes.
    /// </summary>
    PartiallyApplied,

    /// <summary>
    /// Invoice has been fully paid.
    /// Amount paid = total amount.
    /// </summary>
    Paid,

    /// <summary>
    /// Credit note has been fully applied to invoice(s).
    /// Amount applied = total credit amount.
    /// Functionally equivalent to "Paid" for credit notes.
    /// </summary>
    FullyApplied,

    /// <summary>
    /// Invoice/credit note has been voided/cancelled.
    /// A reversing ledger entry has been created.
    /// This status can be reached from Draft, Sent/Issued, or PartiallyPaid/PartiallyApplied.
    /// </summary>
    Voided
}

public static class InvoiceStatusExtensions
{
    public static string ToStringValue(this InvoiceStatus status) => status switch
    {
        InvoiceStatus.Draft => "draft",
        InvoiceStatus.Sent => "sent",
        InvoiceStatus.Issued => "issued",
        InvoiceStatus.PartiallyPaid => "partially_paid",
        InvoiceStatus.PartiallyApplied => "partially_applied",
        InvoiceStatus.Paid => "paid",
        InvoiceStatus.FullyApplied => "fully_applied",
        InvoiceStatus.Voided => "voided",
        _ => throw new ArgumentOutOfRangeException(nameof(status))
    };

    public static InvoiceStatus FromString(string value) => value.ToLowerInvariant() switch
    {
        "draft" => InvoiceStatus.Draft,
        "sent" => InvoiceStatus.Sent,
        "issued" => InvoiceStatus.Issued,
        "partially_paid" => InvoiceStatus.PartiallyPaid,
        "partially_applied" => InvoiceStatus.PartiallyApplied,
        "paid" => InvoiceStatus.Paid,
        "fully_applied" => InvoiceStatus.FullyApplied,
        "voided" => InvoiceStatus.Voided,
        _ => throw new ArgumentException($"Unknown invoice status: {value}", nameof(value))
    };

    /// <summary>
    /// Returns true if the invoice/credit note can be modified (lines added/removed).
    /// Only drafts can be modified.
    /// </summary>
    public static bool CanModify(this InvoiceStatus status) => status == InvoiceStatus.Draft;

    /// <summary>
    /// Returns true if the invoice can be sent.
    /// Only drafts with at least one line can be sent.
    /// </summary>
    public static bool CanSend(this InvoiceStatus status) => status == InvoiceStatus.Draft;

    /// <summary>
    /// Returns true if the credit note can be issued.
    /// Only drafts with at least one line can be issued.
    /// </summary>
    public static bool CanIssue(this InvoiceStatus status) => status == InvoiceStatus.Draft;

    /// <summary>
    /// Returns true if the invoice can receive payments.
    /// Sent and PartiallyPaid invoices can receive payments.
    /// </summary>
    public static bool CanReceivePayment(this InvoiceStatus status) =>
        status == InvoiceStatus.Sent || status == InvoiceStatus.PartiallyPaid;

    /// <summary>
    /// Returns true if the credit note can be applied to invoices.
    /// Issued and PartiallyApplied credit notes can be applied.
    /// </summary>
    public static bool CanApplyCredit(this InvoiceStatus status) =>
        status == InvoiceStatus.Issued || status == InvoiceStatus.PartiallyApplied;

    /// <summary>
    /// Returns true if the invoice/credit note can be voided.
    /// Cannot void a fully paid/applied or already voided document.
    /// </summary>
    public static bool CanVoid(this InvoiceStatus status) =>
        status != InvoiceStatus.Paid &&
        status != InvoiceStatus.FullyApplied &&
        status != InvoiceStatus.Voided;

    /// <summary>
    /// Returns true if the status represents a "sent" or "issued" state.
    /// </summary>
    public static bool IsSentOrIssued(this InvoiceStatus status) =>
        status == InvoiceStatus.Sent || status == InvoiceStatus.Issued;

    /// <summary>
    /// Returns true if the status represents a "paid" or "fully applied" state.
    /// </summary>
    public static bool IsPaidOrFullyApplied(this InvoiceStatus status) =>
        status == InvoiceStatus.Paid || status == InvoiceStatus.FullyApplied;

    /// <summary>
    /// Returns true if the status represents a "partial" state (partial payment or partial application).
    /// </summary>
    public static bool IsPartial(this InvoiceStatus status) =>
        status == InvoiceStatus.PartiallyPaid || status == InvoiceStatus.PartiallyApplied;
}
