namespace CashLineERP.Domain.Finance;

public sealed class PaymentSettlementException
{
    public long Id { get; private set; }
    public long PaymentSettlementBatchId { get; private set; }
    public long PaymentSettlementBatchLineId { get; private set; }
    public string SourceDocumentType { get; private set; } = string.Empty;
    public long SourceDocumentId { get; private set; }
    public string SourceDocumentNumber { get; private set; } = string.Empty;
    public long? PartyId { get; private set; }
    public string ExceptionCode { get; private set; } = string.Empty;
    public string StatusCode { get; private set; } = "OPEN";
    public decimal DifferenceAmount { get; private set; }
    public string? Notes { get; private set; }
    public string? ResolutionCode { get; private set; }
    public DateTime? ResolvedAtUtc { get; private set; }
    public long? ResolvedByUserId { get; private set; }

    public PaymentSettlementException(
        long paymentSettlementBatchId,
        long paymentSettlementBatchLineId,
        string sourceDocumentType,
        long sourceDocumentId,
        string sourceDocumentNumber,
        long? partyId,
        string exceptionCode,
        decimal differenceAmount,
        string? notes)
    {
        if (paymentSettlementBatchLineId <= 0)
        {
            throw new InvalidOperationException("Payment settlement exception requires a valid settlement line.");
        }

        if (string.IsNullOrWhiteSpace(exceptionCode))
        {
            throw new InvalidOperationException("Payment settlement exception code is required.");
        }

        PaymentSettlementBatchId = paymentSettlementBatchId;
        PaymentSettlementBatchLineId = paymentSettlementBatchLineId;
        SourceDocumentType = sourceDocumentType.Trim().ToUpperInvariant();
        SourceDocumentId = sourceDocumentId;
        SourceDocumentNumber = sourceDocumentNumber.Trim().ToUpperInvariant();
        PartyId = partyId;
        ExceptionCode = exceptionCode.Trim().ToUpperInvariant();
        DifferenceAmount = differenceAmount;
        Notes = string.IsNullOrWhiteSpace(notes) ? null : notes.Trim();
    }

    private PaymentSettlementException()
    {
    }

    public void Confirm(long actorUserId, string? notes)
    {
        Resolve(actorUserId, "CONFIRMED", notes);
    }

    public void Override(long actorUserId, string notes)
    {
        if (string.IsNullOrWhiteSpace(notes))
        {
            throw new InvalidOperationException("Payment settlement exception override requires finance notes.");
        }

        Resolve(actorUserId, "OVERRIDDEN", notes);
    }

    private void Resolve(long actorUserId, string resolutionCode, string? notes)
    {
        if (!string.Equals(StatusCode, "OPEN", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only open payment settlement exceptions can be resolved.");
        }

        StatusCode = "RESOLVED";
        ResolutionCode = resolutionCode;
        Notes = string.IsNullOrWhiteSpace(notes) ? Notes : notes.Trim();
        ResolvedAtUtc = DateTime.UtcNow;
        ResolvedByUserId = actorUserId;
    }
}
