EmailItem.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using System.IO.Abstractions;
  2. namespace qdr.app.studiou.orders2printpack.EmailStorage
  3. {
  4. /// <summary>
  5. /// Helper class for working with email items and their file operations.
  6. /// Provides a convenient wrapper around MetadataItem with additional functionality
  7. /// for file system operations, validation, and state management.
  8. /// </summary>
  9. public class EmailItem
  10. {
  11. #region Private Fields
  12. /// <summary>
  13. /// The underlying metadata item containing email and processing information
  14. /// </summary>
  15. private readonly MetadataItem _metadata;
  16. /// <summary>
  17. /// Base directory path where email item directories are located
  18. /// </summary>
  19. private readonly string _basePath;
  20. /// <summary>
  21. /// File system abstraction for testable file operations
  22. /// </summary>
  23. private readonly IFileSystem _fileSystem;
  24. #endregion
  25. #region Constructor
  26. /// <summary>
  27. /// Initializes a new instance of the EmailItem class
  28. /// </summary>
  29. /// <param name="metadata">The metadata item containing email information</param>
  30. /// <param name="basePath">Base directory path for file operations</param>
  31. /// <param name="fileSystem">File system abstraction for file operations</param>
  32. /// <exception cref="ArgumentNullException">Thrown when metadata, basePath, or fileSystem is null</exception>
  33. public EmailItem(MetadataItem metadata, string basePath, IFileSystem fileSystem)
  34. {
  35. _metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
  36. _basePath = basePath ?? throw new ArgumentNullException(nameof(basePath));
  37. _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
  38. }
  39. #endregion
  40. #region Public Properties
  41. /// <summary>
  42. /// Gets the underlying metadata item
  43. /// </summary>
  44. public MetadataItem Metadata => _metadata;
  45. /// <summary>
  46. /// Gets the full directory path for this email item
  47. /// Combines the base path with the directory name from metadata
  48. /// </summary>
  49. public string DirectoryPath => _fileSystem.Path.Combine(_basePath, _metadata.DirectoryName);
  50. /// <summary>
  51. /// Gets a value indicating whether the directory exists on the file system
  52. /// </summary>
  53. public bool DirectoryExists => _fileSystem.Directory.Exists(DirectoryPath);
  54. /// <summary>
  55. /// Gets all JPG files in the email item's directory
  56. /// Returns an empty enumerable if the directory doesn't exist
  57. /// </summary>
  58. public IEnumerable<string> JpgFiles =>
  59. DirectoryExists ? _fileSystem.Directory.GetFiles(DirectoryPath, "*.jpg") : Enumerable.Empty<string>();
  60. /// <summary>
  61. /// Gets the count of JPG files in the directory
  62. /// </summary>
  63. public int FileCount => JpgFiles.Count();
  64. /// <summary>
  65. /// Gets the total size in bytes of all JPG files in the directory
  66. /// </summary>
  67. public long TotalFileSize => JpgFiles.Sum(f => _fileSystem.FileInfo.New(f).Length);
  68. /// <summary>
  69. /// Gets a value indicating whether this item is ready for processing
  70. /// Checks that the state is Ready, directory exists, and contains files
  71. /// </summary>
  72. public bool IsReadyForProcessing =>
  73. _metadata.State == EmailStateEnum.Ready && DirectoryExists && FileCount > 0;
  74. /// <summary>
  75. /// Gets a value indicating whether this item can be reset
  76. /// Items cannot be reset while they are being processed or uploaded
  77. /// </summary>
  78. public bool CanBeReset =>
  79. _metadata.State != EmailStateEnum.Processing &&
  80. _metadata.State != EmailStateEnum.Uploading;
  81. /// <summary>
  82. /// Gets the normalized filename for the recipient (safe for file system)
  83. /// Replaces @ with _at_ and . with _dot_ to create filesystem-safe names
  84. /// </summary>
  85. public string NormalizedRecipient =>
  86. _metadata.EmailAddress.Replace("@", "_at_").Replace(".", "_dot_");
  87. #endregion
  88. #region Public Methods
  89. /// <summary>
  90. /// Generates the zip filename for this item based on recipient and timestamp
  91. /// </summary>
  92. /// <param name="timestamp">Optional timestamp to use, defaults to current time</param>
  93. /// <returns>A formatted zip filename in the format: studioufoto_{recipient}_{timestamp}.zip</returns>
  94. public string GenerateZipFileName(DateTime? timestamp = null) =>
  95. $"studioufoto_{NormalizedRecipient}_{(timestamp ?? DateTime.Now):yyyyMMdd_HHmmss}.zip";
  96. /// <summary>
  97. /// Validates that the item is in a consistent state for processing
  98. /// </summary>
  99. /// <returns>A tuple containing validation result and list of issues found</returns>
  100. public (bool IsValid, List<string> Issues) Validate()
  101. {
  102. var issues = new List<string>();
  103. // Check required email address
  104. if (string.IsNullOrEmpty(_metadata.EmailAddress))
  105. issues.Add("Email address is required");
  106. // Check directory and files existence
  107. if (!DirectoryExists)
  108. issues.Add($"Directory does not exist: {DirectoryPath}");
  109. else if (FileCount == 0)
  110. issues.Add("No JPG files found in directory");
  111. // Check required email content
  112. if (string.IsNullOrEmpty(_metadata.Subject))
  113. issues.Add("Email subject is required");
  114. if (string.IsNullOrEmpty(_metadata.Body))
  115. issues.Add("Email body is required");
  116. return (issues.Count == 0, issues);
  117. }
  118. /// <summary>
  119. /// Gets a summary of the item for display purposes
  120. /// Includes recipient, file count, current state, and associated orders
  121. /// </summary>
  122. /// <returns>A formatted summary string</returns>
  123. public string GetSummary() =>
  124. $"{_metadata.EmailAddress} - {FileCount} files, {_metadata.State}, Orders: {_metadata.Orders}";
  125. /// <summary>
  126. /// Gets detailed file information for all JPG files in the directory
  127. /// </summary>
  128. /// <returns>Collection of file information objects</returns>
  129. public IEnumerable<IFileInfo> GetFileDetails() =>
  130. JpgFiles.Select(f => _fileSystem.FileInfo.New(f));
  131. /// <summary>
  132. /// Checks if a specific file exists in the email item's directory
  133. /// </summary>
  134. /// <param name="fileName">Name of the file to check</param>
  135. /// <returns>True if the file exists, false otherwise</returns>
  136. public bool FileExists(string fileName)
  137. {
  138. if (string.IsNullOrEmpty(fileName))
  139. return false;
  140. var filePath = _fileSystem.Path.Combine(DirectoryPath, fileName);
  141. return _fileSystem.File.Exists(filePath);
  142. }
  143. /// <summary>
  144. /// Gets the full path for a file within the email item's directory
  145. /// </summary>
  146. /// <param name="fileName">Name of the file</param>
  147. /// <returns>Full path to the file</returns>
  148. public string GetFilePath(string fileName) =>
  149. _fileSystem.Path.Combine(DirectoryPath, fileName);
  150. /// <summary>
  151. /// Creates the email item's directory if it doesn't exist
  152. /// </summary>
  153. /// <returns>True if directory was created or already exists, false if creation failed</returns>
  154. public bool EnsureDirectoryExists()
  155. {
  156. try
  157. {
  158. if (!DirectoryExists)
  159. {
  160. _fileSystem.Directory.CreateDirectory(DirectoryPath);
  161. }
  162. return true;
  163. }
  164. catch
  165. {
  166. return false;
  167. }
  168. }
  169. /// <summary>
  170. /// Gets file information for a specific file in the directory
  171. /// </summary>
  172. /// <param name="fileName">Name of the file</param>
  173. /// <returns>File information object, or null if file doesn't exist</returns>
  174. public IFileInfo? GetFileInfo(string fileName)
  175. {
  176. if (string.IsNullOrEmpty(fileName))
  177. return null;
  178. var filePath = GetFilePath(fileName);
  179. return _fileSystem.File.Exists(filePath) ? _fileSystem.FileInfo.New(filePath) : null;
  180. }
  181. #endregion
  182. }
  183. }