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; } = ""; /// Null for Error rows caused by a login/kind conflict, and for Skipped (unrecognized file) rows. public ArchiveKind? 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); public string KindDisplay { get { switch (Kind) { case ArchiveKind.SshAccess: return "Новый доступ (ключ + конфиг)"; case ArchiveKind.ConfigUpdate: return "Обновление конфига"; default: return "-"; } } } public override string ToString() => $"{Login} <{Email}> [{Status}]"; } }