using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace LittleShop.Models; public enum MessageDirection { AdminToCustomer = 0, CustomerToAdmin = 1 } public enum MessageStatus { Pending = 0, // Message created, waiting to be sent Sent = 1, // Message delivered to customer Delivered = 2, // Message acknowledged by bot/platform Read = 3, // Customer has read the message Failed = 4 // Delivery failed } public enum MessageType { OrderUpdate = 0, // Status update about an order PaymentReminder = 1, // Payment reminder ShippingInfo = 2, // Tracking/shipping information CustomerService = 3, // General customer service Marketing = 4, // Marketing/promotional (requires consent) SystemAlert = 5 // System-generated alerts } public class CustomerMessage { [Key] public Guid Id { get; set; } // Relationships [Required] public Guid CustomerId { get; set; } public Guid? OrderId { get; set; } // Optional - message may not be about specific order public Guid? AdminUserId { get; set; } // Which admin sent the message (null for system messages or unidentified admins) // Message Details [Required] public MessageDirection Direction { get; set; } [Required] public MessageType Type { get; set; } [Required] [StringLength(100)] public string Subject { get; set; } = string.Empty; [Required] [StringLength(4000)] // Telegram message limit is ~4096 characters public string Content { get; set; } = string.Empty; public MessageStatus Status { get; set; } = MessageStatus.Pending; // Delivery Information public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? SentAt { get; set; } public DateTime? DeliveredAt { get; set; } public DateTime? ReadAt { get; set; } public DateTime? FailedAt { get; set; } [StringLength(500)] public string? FailureReason { get; set; } public int RetryCount { get; set; } = 0; public DateTime? NextRetryAt { get; set; } // Message Threading (for conversations) public Guid? ParentMessageId { get; set; } // Reply to another message public Guid? ThreadId { get; set; } // Conversation thread identifier // Platform-specific Information [StringLength(100)] public string Platform { get; set; } = "Telegram"; // Telegram, Email, SMS, etc. [StringLength(200)] public string? PlatformMessageId { get; set; } // Telegram message ID, email message ID, etc. // Priority and Scheduling public int Priority { get; set; } = 5; // 1-10, 1 = highest priority public DateTime? ScheduledFor { get; set; } // For scheduled messages public DateTime? ExpiresAt { get; set; } // Message expires if not delivered // Customer Preferences public bool RequiresResponse { get; set; } = false; public bool IsUrgent { get; set; } = false; public bool IsMarketing { get; set; } = false; // Auto-generated flags public bool IsAutoGenerated { get; set; } = false; [StringLength(100)] public string? AutoGenerationTrigger { get; set; } // e.g., "OrderStatusChanged", "PaymentOverdue" // Data retention public bool IsArchived { get; set; } = false; public DateTime? ArchivedAt { get; set; } // Navigation properties public virtual Customer Customer { get; set; } = null!; public virtual Order? Order { get; set; } public virtual User? AdminUser { get; set; } public virtual CustomerMessage? ParentMessage { get; set; } public virtual ICollection Replies { get; set; } = new List(); // Helper methods public bool CanRetry() { return Status == MessageStatus.Failed && RetryCount < 3 && (NextRetryAt == null || DateTime.UtcNow >= NextRetryAt) && (ExpiresAt == null || DateTime.UtcNow < ExpiresAt); } public void MarkAsSent() { Status = MessageStatus.Sent; SentAt = DateTime.UtcNow; } public void MarkAsDelivered(string? platformMessageId = null) { Status = MessageStatus.Delivered; DeliveredAt = DateTime.UtcNow; if (!string.IsNullOrEmpty(platformMessageId)) { PlatformMessageId = platformMessageId; } } public void MarkAsRead() { Status = MessageStatus.Read; ReadAt = DateTime.UtcNow; } public void MarkAsFailed(string reason) { Status = MessageStatus.Failed; FailedAt = DateTime.UtcNow; FailureReason = reason; RetryCount++; // Exponential backoff for retries if (RetryCount <= 3) { var delayMinutes = Math.Pow(2, RetryCount) * 5; // 10, 20, 40 minutes NextRetryAt = DateTime.UtcNow.AddMinutes(delayMinutes); } } public string GetDisplayTitle() { var prefix = Direction == MessageDirection.AdminToCustomer ? "→" : "←"; var typeStr = Type switch { MessageType.OrderUpdate => "Order Update", MessageType.PaymentReminder => "Payment", MessageType.ShippingInfo => "Shipping", MessageType.CustomerService => "Support", MessageType.Marketing => "Marketing", MessageType.SystemAlert => "System", _ => "Message" }; return $"{prefix} {typeStr}: {Subject}"; } }