using System.Collections.Generic; using System.IO; namespace BulkArchiveMailer.Core { /// Status of one preview row / send attempt. public enum SendStatus { /// Exactly one archive found for this login, nothing sent for it yet - safe to send. Ready, /// A prior log entry shows this exact archive (by SHA-256) was already sent to this address. AlreadySent, /// Blocking data problem (e.g. both archive kinds present for one login) - never sendable. Error, /// File in the folder did not match the expected naming pattern - listed for visibility, not sendable. Skipped } /// /// One row of the send preview: everything needed to either display the /// row in the grid or hand it to MailSender. /// public sealed class SendJob { public string Login { get; set; } = ""; public string Email { get; set; } = ""; /// File suffix ("ssh_access", "config_update", or any other configured template's FileSuffix). Null for Error rows caused by a login/kind conflict, and for Skipped (unrecognized file) rows. public string Kind { get; set; } /// Null for conflict Error rows (see ) - otherwise the single archive to attach. public string ArchivePath { get; set; } /// Populated only for the "both kinds present" Error case: every archive path found for that login. public IReadOnlyList ConflictingArchivePaths { get; set; } public string Subject { get; set; } = ""; public string BodyHtml { get; set; } = ""; public SendStatus Status { get; set; } = SendStatus.Ready; /// Human-readable explanation, set for Error/Skipped rows and for AlreadySent rows (when it was sent). public string ErrorMessage { get; set; } /// SHA-256 of ArchivePath's current bytes, computed when a SendLogStore is available. Used for the AlreadySent dedup key. public string FileSha256 { get; set; } /// Whether this row is even eligible to be checked/sent in the preview grid. public bool IsSelectable => Status == SendStatus.Ready || Status == SendStatus.AlreadySent; public string FileName => string.IsNullOrEmpty(ArchivePath) ? "" : Path.GetFileName(ArchivePath); /// /// Human-readable label for the matched template (its /// DisplayName), explicitly set by ArchiveScanner when it renders /// this job - "-" for conflict Error rows and Skipped rows, which /// never had a matched template. No longer computed from a fixed /// enum switch, since the set of templates is now open-ended. /// public string KindDisplay { get; set; } = "-"; public override string ToString() => $"{Login} <{Email}> [{Status}]"; } }