using CashLineERP.Domain.Common;

namespace CashLineERP.Domain.Integration;

public sealed class InboxMessage : Entity
{
    public long Id { get; private set; }
    public string MessageNumber { get; private set; } = string.Empty;
    public long IntegrationEndpointId { get; private set; }
    public string MessageType { get; private set; } = string.Empty;
    public string? ExternalMessageId { get; private set; }
    public string? CorrelationKey { get; private set; }
    public string PayloadText { get; private set; } = string.Empty;
    public string StatusCode { get; private set; } = "RECEIVED";
    public int AttemptNumber { get; private set; }
    public DateTime AvailableAtUtc { get; private set; }
    public DateTime? ProcessedAtUtc { get; private set; }
    public string? ProcessedByProcess { get; private set; }
    public string? LastErrorText { get; private set; }

    public InboxMessage(
        string messageNumber,
        long integrationEndpointId,
        string messageType,
        string? externalMessageId,
        string? correlationKey,
        string payloadText,
        DateTime availableAtUtc)
    {
        if (string.IsNullOrWhiteSpace(messageNumber))
        {
            throw new InvalidOperationException("Inbox message number is required.");
        }

        if (integrationEndpointId <= 0)
        {
            throw new InvalidOperationException("Inbox message endpoint ID must be greater than zero.");
        }

        if (string.IsNullOrWhiteSpace(messageType))
        {
            throw new InvalidOperationException("Inbox message type is required.");
        }

        if (string.IsNullOrWhiteSpace(payloadText))
        {
            throw new InvalidOperationException("Inbox message payload is required.");
        }

        MessageNumber = messageNumber.Trim().ToUpperInvariant();
        IntegrationEndpointId = integrationEndpointId;
        MessageType = messageType.Trim().ToUpperInvariant();
        ExternalMessageId = string.IsNullOrWhiteSpace(externalMessageId) ? null : externalMessageId.Trim();
        CorrelationKey = string.IsNullOrWhiteSpace(correlationKey) ? null : correlationKey.Trim();
        PayloadText = payloadText;
        AvailableAtUtc = availableAtUtc;
    }

    private InboxMessage()
    {
    }

    public void MarkProcessed(string processorCode)
    {
        if (string.IsNullOrWhiteSpace(processorCode))
        {
            throw new InvalidOperationException("Inbox processor code is required.");
        }

        if (StatusCode != "RECEIVED" && StatusCode != "RETRY")
        {
            throw new InvalidOperationException("Only received or retry inbox messages can be processed.");
        }

        StatusCode = "PROCESSED";
        AttemptNumber += 1;
        ProcessedAtUtc = DateTime.UtcNow;
        ProcessedByProcess = processorCode.Trim().ToUpperInvariant();
        LastErrorText = null;
        Touch(null);
    }

    public void ScheduleRetry(string errorText, DateTime nextAttemptAtUtc)
    {
        StatusCode = "RETRY";
        AttemptNumber += 1;
        LastErrorText = string.IsNullOrWhiteSpace(errorText) ? null : errorText.Trim();
        AvailableAtUtc = nextAttemptAtUtc;
        Touch(null);
    }

    public void MarkFailed(string errorText)
    {
        StatusCode = "FAILED";
        AttemptNumber += 1;
        LastErrorText = string.IsNullOrWhiteSpace(errorText) ? null : errorText.Trim();
        Touch(null);
    }

    public void MoveToDeadLetter(string reasonText)
    {
        if (string.IsNullOrWhiteSpace(reasonText))
        {
            throw new InvalidOperationException("Dead-letter reason is required.");
        }

        if (StatusCode != "RECEIVED" && StatusCode != "RETRY" && StatusCode != "FAILED")
        {
            throw new InvalidOperationException("Only received, retry, or failed inbox messages can be moved to dead-letter.");
        }

        StatusCode = "DEAD_LETTER";
        LastErrorText = reasonText.Trim();
        Touch(null);
    }

    public void Replay(string replayReasonText, DateTime nextAttemptAtUtc)
    {
        if (StatusCode != "DEAD_LETTER")
        {
            throw new InvalidOperationException("Only dead-letter inbox messages can be replayed.");
        }

        StatusCode = "RETRY";
        AvailableAtUtc = nextAttemptAtUtc;
        LastErrorText = string.IsNullOrWhiteSpace(replayReasonText)
            ? null
            : $"REPLAY REQUESTED: {replayReasonText.Trim()}";
        Touch(null);
    }
}
