namespace Books.Api.Invoicing.Services;

/// <summary>
/// Service for matching bank transactions to invoices.
/// Uses multiple signals: amount, reference, counterparty name.
/// </summary>
public interface IPaymentMatchingService
{
    /// <summary>
    /// Generates suggested matches for a specific bank transaction.
    /// </summary>
    Task<IReadOnlyList<SuggestedPaymentMatch>> SuggestMatchesAsync(
        string bankTransactionId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Generates suggested matches for all pending bank transactions in a company.
    /// </summary>
    Task<IReadOnlyList<SuggestedPaymentMatch>> SuggestMatchesForCompanyAsync(
        string companyId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Confirms a suggested match and creates a payment allocation.
    /// Also updates invoice payment status and marks bank transaction as booked.
    /// </summary>
    Task<PaymentAllocationResult> ConfirmMatchAsync(
        string bankTransactionId,
        string invoiceId,
        decimal amount,
        string matchMethod,
        string userId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Rejects a suggested match.
    /// </summary>
    Task RejectMatchAsync(
        string bankTransactionId,
        string invoiceId,
        string userId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Removes a payment allocation (unmatch).
    /// </summary>
    Task<bool> RemoveAllocationAsync(
        string allocationId,
        string userId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Gets all pending suggested matches for a company.
    /// </summary>
    Task<IReadOnlyList<SuggestedPaymentMatch>> GetPendingSuggestionsAsync(
        string companyId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Gets all payment allocations for an invoice.
    /// </summary>
    Task<IReadOnlyList<PaymentAllocation>> GetAllocationsForInvoiceAsync(
        string invoiceId,
        CancellationToken cancellationToken = default);
}

/// <summary>
/// A suggested match between a bank transaction and an invoice.
/// </summary>
public record SuggestedPaymentMatch
{
    public string Id { get; init; } = string.Empty;
    public string CompanyId { get; init; } = string.Empty;
    public string BankTransactionId { get; init; } = string.Empty;
    public string InvoiceId { get; init; } = string.Empty;

    // Bank transaction details (denormalized for UI)
    public decimal BankTransactionAmount { get; init; }
    public DateOnly BankTransactionDate { get; init; }
    public string? BankTransactionDescription { get; init; }
    public string? BankTransactionCounterparty { get; init; }

    // Invoice details (denormalized for UI)
    public string InvoiceNumber { get; init; } = string.Empty;
    public string CustomerName { get; init; } = string.Empty;
    public decimal InvoiceAmountRemaining { get; init; }
    public DateOnly? InvoiceDueDate { get; init; }

    // Match scoring
    public decimal Confidence { get; init; }
    public decimal SuggestedAmount { get; init; }
    public IReadOnlyList<MatchReason> MatchReasons { get; init; } = [];

    // Status
    public string Status { get; init; } = "pending";
    public DateTimeOffset CreatedAt { get; init; }
}

/// <summary>
/// A reason why a match was suggested, with its contribution to the confidence score.
/// </summary>
public record MatchReason
{
    public string Reason { get; init; } = string.Empty;
    public string Description { get; init; } = string.Empty;
    public decimal Score { get; init; }
}

/// <summary>
/// A confirmed payment allocation between a bank transaction and an invoice.
/// </summary>
public record PaymentAllocation
{
    public string Id { get; init; } = string.Empty;
    public string CompanyId { get; init; } = string.Empty;
    public string BankTransactionId { get; init; } = string.Empty;
    public string? InvoiceId { get; init; }
    public string? CreditNoteId { get; init; }
    public decimal Amount { get; init; }
    public string AllocationType { get; init; } = "payment";
    public string MatchMethod { get; init; } = "manual";
    public decimal? MatchConfidence { get; init; }
    public string? LedgerTransactionId { get; init; }
    public DateTimeOffset CreatedAt { get; init; }
    public string CreatedBy { get; init; } = string.Empty;
}

/// <summary>
/// Result of confirming a payment match.
/// </summary>
public record PaymentAllocationResult
{
    public bool Success { get; init; }
    public PaymentAllocation? Allocation { get; init; }
    public string? ErrorCode { get; init; }
    public string? ErrorMessage { get; init; }

    public static PaymentAllocationResult Succeeded(PaymentAllocation allocation) =>
        new() { Success = true, Allocation = allocation };

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