using CashLineERP.Domain.Common;

namespace CashLineERP.Domain.Notifications;

public sealed class NotificationQueueItem : Entity
{
    public long Id { get; private set; }
    public long NotificationTemplateId { get; private set; }
    public string TemplateCode { get; private set; } = string.Empty;

    public string ChannelCode { get; private set; } = string.Empty;

    public string RecipientReference { get; private set; } = string.Empty;

    public string? ContextCode { get; private set; }

    public string? RelatedEntityType { get; private set; }

    public long? RelatedEntityId { get; private set; }

    public string? Subject { get; private set; }

    public string Body { get; private set; } = string.Empty;

    public string StatusCode { get; private set; } = "PENDING";

    public int AttemptNumber { get; private set; }

    public DateTime? LastAttemptAtUtc { get; private set; }

    public DateTime? NextAttemptAtUtc { get; private set; }

    public DateTime? SentAtUtc { get; private set; }

    public string? SentByProcess { get; private set; }

    public string? ErrorText { get; private set; }

    public NotificationQueueItem(
        long notificationTemplateId,
        string templateCode,
        string channelCode,
        string recipientReference,
        string? contextCode,
        string? relatedEntityType,
        long? relatedEntityId,
        string? subject,
        string body)
    {
        if (notificationTemplateId <= 0)
        {
            throw new InvalidOperationException("Notification template ID is required.");
        }

        if (string.IsNullOrWhiteSpace(templateCode))
        {
            throw new InvalidOperationException("Notification template code is required.");
        }

        if (string.IsNullOrWhiteSpace(channelCode))
        {
            throw new InvalidOperationException("Notification channel is required.");
        }

        if (string.IsNullOrWhiteSpace(recipientReference))
        {
            throw new InvalidOperationException("Notification recipient reference is required.");
        }

        if (string.IsNullOrWhiteSpace(body))
        {
            throw new InvalidOperationException("Notification body is required.");
        }

        NotificationTemplateId = notificationTemplateId;
        TemplateCode = templateCode.Trim().ToUpperInvariant();
        ChannelCode = channelCode;
        RecipientReference = recipientReference;
        ContextCode = string.IsNullOrWhiteSpace(contextCode) ? null : contextCode.Trim().ToUpperInvariant();
        RelatedEntityType = string.IsNullOrWhiteSpace(relatedEntityType) ? null : relatedEntityType.Trim().ToUpperInvariant();
        RelatedEntityId = relatedEntityId;
        Subject = subject;
        Body = body;
    }

    private NotificationQueueItem()
    {
    }

    public void MarkSent(string senderCode)
    {
        if (string.IsNullOrWhiteSpace(senderCode))
        {
            throw new InvalidOperationException("Notification sender code is required.");
        }

        if (StatusCode != "PENDING" && StatusCode != "RETRY")
        {
            throw new InvalidOperationException("Only pending or retry notifications can be marked as sent.");
        }

        StatusCode = "SENT";
        AttemptNumber += 1;
        LastAttemptAtUtc = DateTime.UtcNow;
        NextAttemptAtUtc = null;
        SentAtUtc = DateTime.UtcNow;
        SentByProcess = senderCode.Trim().ToUpperInvariant();
        ErrorText = null;
    }

    public void ScheduleRetry(string errorText, DateTime nextAttemptAtUtc)
    {
        StatusCode = "RETRY";
        AttemptNumber += 1;
        LastAttemptAtUtc = DateTime.UtcNow;
        NextAttemptAtUtc = nextAttemptAtUtc;
        ErrorText = string.IsNullOrWhiteSpace(errorText) ? null : errorText.Trim();
    }

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

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

        StatusCode = "DEAD_LETTER";
        ErrorText = reasonText.Trim();
    }

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

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

    public void MarkFailed(string errorText)
    {
        StatusCode = "FAILED";
        AttemptNumber += 1;
        LastAttemptAtUtc = DateTime.UtcNow;
        ErrorText = string.IsNullOrWhiteSpace(errorText) ? null : errorText.Trim();
    }
}
