using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Domain.Entities.Donations.ValueObject; using Domain.Entities.Members; namespace Domain.Entities.Donations; public class CrewInvitation { public const int ExpirationHours = 24; [ForeignKey(nameof(CrewID))] public virtual Crew? Crew { get; private set; } [ForeignKey(nameof(TargetMemberID))] public virtual Member? TargetMember { get; private set; } [ForeignKey(nameof(InvitedByMemberID))] public virtual Member? InvitedByMember { get; private set; } [Key] public int ID { get; private set; } public int CrewID { get; private set; } public int TargetMemberID { get; private set; } public int InvitedByMemberID { get; private set; } public string Nickname { get; private set; } = default!; public string? Role { get; private set; } public int SortOrder { get; private set; } public CrewInviteStatus Status { get; private set; } = CrewInviteStatus.Pending; public DateTime InvitedAt { get; private set; } = DateTime.UtcNow; public DateTime ExpiresAt { get; private set; } = DateTime.UtcNow.AddHours(ExpirationHours); public DateTime? RespondedAt { get; private set; } private CrewInvitation() { } public static CrewInvitation Create( int crewID, int targetMemberID, int invitedByMemberID, string nickname, string? role = null, int sortOrder = 0 ) { if (string.IsNullOrWhiteSpace(nickname)) { throw new ArgumentException("Nickname is required.", nameof(nickname)); } var now = DateTime.UtcNow; return new CrewInvitation { CrewID = crewID, TargetMemberID = targetMemberID, InvitedByMemberID = invitedByMemberID, Nickname = nickname, Role = role, SortOrder = sortOrder, Status = CrewInviteStatus.Pending, InvitedAt = now, ExpiresAt = now.AddHours(ExpirationHours) }; } public void Accept() { if (Status != CrewInviteStatus.Pending) { throw new InvalidOperationException($"Cannot accept invitation in {Status} state."); } if (DateTime.UtcNow > ExpiresAt) { throw new InvalidOperationException("Invitation has expired."); } Status = CrewInviteStatus.Accepted; RespondedAt = DateTime.UtcNow; } public void Decline() { if (Status != CrewInviteStatus.Pending) { throw new InvalidOperationException($"Cannot decline invitation in {Status} state."); } Status = CrewInviteStatus.Declined; RespondedAt = DateTime.UtcNow; } public void MarkExpired() { if (Status != CrewInviteStatus.Pending) { return; } Status = CrewInviteStatus.Expired; RespondedAt = DateTime.UtcNow; } public bool IsExpired() => Status == CrewInviteStatus.Pending && DateTime.UtcNow > ExpiresAt; }