using Books.Api.Domain.Invoices;

namespace Books.Api.Invoicing.Services;

/// <summary>
/// Service for posting invoices to the Ledger.
/// Handles the creation of ledger transactions when invoices are sent.
/// </summary>
public interface IInvoicePostingService
{
    /// <summary>
    /// Posts an invoice to the Ledger.
    /// Creates entries:
    /// - Debit: Customer sub-ledger account (1900-XXXX)
    /// - Credit: Revenue account(s) per line
    /// - Credit: VAT account (5611 Salgsmoms) if applicable
    /// </summary>
    /// <param name="invoiceId">The invoice ID</param>
    /// <param name="customerId">The customer ID (for looking up sub-ledger account)</param>
    /// <param name="fiscalYearId">The fiscal year to post to</param>
    /// <param name="invoiceNumber">The invoice number for description</param>
    /// <param name="lines">The invoice lines</param>
    /// <param name="defaultRevenueAccountId">Default revenue account if line doesn't specify one</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>The ledger transaction ID if successful</returns>
    Task<InvoicePostingResult> PostInvoiceAsync(
        string invoiceId,
        string customerId,
        string fiscalYearId,
        string invoiceNumber,
        IReadOnlyList<InvoiceLine> lines,
        string defaultRevenueAccountId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Reverses an invoice posting in the Ledger.
    /// Called when voiding an invoice that has already been sent.
    /// </summary>
    /// <param name="invoiceId">The invoice ID</param>
    /// <param name="originalTransactionId">The original ledger transaction ID to reverse</param>
    /// <param name="reason">Reason for voiding</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>The reversal transaction ID if successful</returns>
    Task<InvoicePostingResult> ReverseInvoiceAsync(
        string invoiceId,
        string originalTransactionId,
        string reason,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Posts a payment receipt to the Ledger.
    /// Creates entries:
    /// - Debit: Bank account
    /// - Credit: Customer sub-ledger account (1900-XXXX)
    /// </summary>
    Task<InvoicePostingResult> PostPaymentAsync(
        string invoiceId,
        string customerId,
        string bankAccountId,
        decimal amount,
        string fiscalYearId,
        string? reference,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Posts a credit note to the Ledger.
    /// Creates entries (opposite of invoice):
    /// - Credit: Customer sub-ledger account (1900-XXXX)
    /// - Debit: Revenue account(s) per line
    /// - Debit: VAT account (5611 Salgsmoms) if applicable
    /// </summary>
    Task<InvoicePostingResult> PostCreditNoteAsync(
        string creditNoteId,
        string customerId,
        string fiscalYearId,
        string creditNoteNumber,
        IReadOnlyList<InvoiceLine> lines,
        string defaultRevenueAccountId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Posts the application of a credit note to an invoice.
    /// Creates entries:
    /// - Debit: Customer sub-ledger account (from credit note)
    /// - Credit: Customer sub-ledger account (from invoice)
    /// This effectively offsets the two balances.
    /// </summary>
    Task<InvoicePostingResult> PostCreditApplicationAsync(
        string creditNoteId,
        string invoiceId,
        string customerId,
        decimal amount,
        string fiscalYearId,
        CancellationToken cancellationToken = default);
}

public record InvoicePostingResult
{
    public bool Success { get; init; }
    public string? TransactionId { get; init; }
    public string? ErrorCode { get; init; }
    public string? ErrorMessage { get; init; }

    public static InvoicePostingResult Succeeded(string transactionId) =>
        new() { Success = true, TransactionId = transactionId };

    public static InvoicePostingResult Failed(string errorCode, string errorMessage) =>
        new() { Success = false, ErrorCode = errorCode, ErrorMessage = errorMessage };
}
