using CashLineERP.Domain.Common;

namespace CashLineERP.Domain.Finance;

public sealed class CustomerPaymentRequest : Entity
{
    public long Id { get; private set; }
    public string PaymentRequestNumber { get; private set; } = string.Empty;
    public long CustomerId { get; private set; }
    public long AccountsReceivableInvoiceId { get; private set; }
    public long PaymentChannelConfigurationId { get; private set; }
    public DateOnly PaymentDate { get; private set; }
    public string CurrencyCode { get; private set; } = "EGP";
    public decimal AmountAmount { get; private set; }
    public string StatusCode { get; private set; } = "DRAFT";
    public string? ReferenceNumber { get; private set; }
    public string? Notes { get; private set; }
    public string? CorrelationKey { get; private set; }
    public long? SettledAccountsReceivableReceiptId { get; private set; }
    public DateTime? SubmittedAtUtc { get; private set; }
    public long? SubmittedByUserId { get; private set; }
    public DateTime? CompletedAtUtc { get; private set; }
    public string? LastErrorText { get; private set; }

    public CustomerPaymentRequest(
        string paymentRequestNumber,
        long customerId,
        long accountsReceivableInvoiceId,
        long paymentChannelConfigurationId,
        DateOnly paymentDate,
        string currencyCode,
        decimal amountAmount,
        string? referenceNumber,
        string? notes)
    {
        if (string.IsNullOrWhiteSpace(paymentRequestNumber))
        {
            throw new InvalidOperationException("Customer payment request number is required.");
        }

        if (customerId <= 0 || accountsReceivableInvoiceId <= 0 || paymentChannelConfigurationId <= 0)
        {
            throw new InvalidOperationException("Customer payment request requires valid customer, invoice, and payment channel identifiers.");
        }

        if (string.IsNullOrWhiteSpace(currencyCode))
        {
            throw new InvalidOperationException("Customer payment request currency is required.");
        }

        if (amountAmount <= 0m)
        {
            throw new InvalidOperationException("Customer payment request amount must be greater than zero.");
        }

        PaymentRequestNumber = paymentRequestNumber.Trim().ToUpperInvariant();
        CustomerId = customerId;
        AccountsReceivableInvoiceId = accountsReceivableInvoiceId;
        PaymentChannelConfigurationId = paymentChannelConfigurationId;
        PaymentDate = paymentDate;
        CurrencyCode = currencyCode.Trim().ToUpperInvariant();
        AmountAmount = decimal.Round(amountAmount, 2, MidpointRounding.AwayFromZero);
        ReferenceNumber = string.IsNullOrWhiteSpace(referenceNumber) ? null : referenceNumber.Trim();
        Notes = string.IsNullOrWhiteSpace(notes) ? null : notes.Trim();
    }

    private CustomerPaymentRequest()
    {
    }

    public void Submit(long actorUserId, string correlationKey)
    {
        if (StatusCode != "DRAFT" && StatusCode != "FAILED")
        {
            throw new InvalidOperationException("Only draft or failed customer payment requests can be submitted.");
        }

        if (string.IsNullOrWhiteSpace(correlationKey))
        {
            throw new InvalidOperationException("Customer payment request correlation key is required.");
        }

        StatusCode = "PENDING_CUSTOMER_ACTION";
        CorrelationKey = correlationKey.Trim();
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        CompletedAtUtc = null;
        LastErrorText = null;
        Touch(actorUserId);
    }

    public void MarkProcessing()
    {
        if (StatusCode == "PAID")
        {
            return;
        }

        StatusCode = "PROCESSING";
        LastErrorText = null;
        Touch(null);
    }

    public void MarkPaid(long accountsReceivableReceiptId)
    {
        if (accountsReceivableReceiptId <= 0)
        {
            throw new InvalidOperationException("Customer payment request settlement requires a valid accounts receivable receipt.");
        }

        StatusCode = "PAID";
        SettledAccountsReceivableReceiptId = accountsReceivableReceiptId;
        CompletedAtUtc = DateTime.UtcNow;
        LastErrorText = null;
        Touch(null);
    }

    public void MarkFailed(string errorText)
    {
        if (string.IsNullOrWhiteSpace(errorText))
        {
            throw new InvalidOperationException("Customer payment request failure reason is required.");
        }

        StatusCode = "FAILED";
        CompletedAtUtc = DateTime.UtcNow;
        LastErrorText = errorText.Trim();
        Touch(null);
    }
}
