using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Domain.Entities.Donations.ValueObject; using Domain.Entities.Members; namespace Domain.Entities.Donations; public class DonationAlert { [ForeignKey(nameof(DonationID))] public virtual Donation Donation { get; private set; } = default!; [ForeignKey(nameof(SponsorMemberID))] public virtual Member? Sponsor { get; private set; } [ForeignKey(nameof(ReceiverMemberID))] public virtual Member? Receiver { get; private set; } private readonly List _attempts = []; public IReadOnlyCollection Attempts => _attempts; [Key] public int ID { get; private set; } public int DonationID { get; private set; } public int SponsorMemberID { get; private set; } public int ReceiverMemberID { get; private set; } public AlertStatus Status { get; private set; } = AlertStatus.Queued; public Guid CorrelationID { get; private set; } = Guid.NewGuid(); public DateTime StartAt { get; private set; } = DateTime.UtcNow; public DateTime? ArrivedAt { get; private set; } public double? TotalDelayMs { get; private set; } public string? IpAddress { get; private set; } public string? UserAgent { get; private set; } public DateTime CreatedAt { get; private set; } = DateTime.UtcNow; private DonationAlert() { } public static DonationAlert Create( int donationID, int sponsorMemberID, int receiverMemberID, string? ipAddress = null, string? userAgent = null ) { return new DonationAlert { DonationID = donationID, SponsorMemberID = sponsorMemberID, ReceiverMemberID = receiverMemberID, IpAddress = ipAddress, UserAgent = userAgent }; } public void MarkPlaying() { Status = AlertStatus.Playing; } public void MarkDelivered() { Status = AlertStatus.Delivered; ArrivedAt = DateTime.UtcNow; TotalDelayMs = (ArrivedAt.Value - StartAt).TotalMilliseconds; } public void MarkSkipped() { if (Status != AlertStatus.Playing) { throw new InvalidOperationException("Only playing alerts can be skipped."); } Status = AlertStatus.Skipped; } public void MarkIgnored() { if (Status == AlertStatus.Playing) { throw new InvalidOperationException("Cannot ignore a playing alert."); } Status = AlertStatus.Ignored; } public void MarkFailed() { Status = AlertStatus.Failed; } public void Resend() { if (Status is not (AlertStatus.Failed or AlertStatus.Skipped)) { throw new InvalidOperationException("Only failed or skipped alerts can be resent."); } Status = AlertStatus.Queued; ArrivedAt = null; TotalDelayMs = null; } }