using System.IO.Abstractions; namespace qdr.app.studiou.orders2printpack.EmailStorage { /// /// Helper class for working with email items and their file operations. /// Provides a convenient wrapper around MetadataItem with additional functionality /// for file system operations, validation, and state management. /// public class EmailItem { #region Private Fields /// /// The underlying metadata item containing email and processing information /// private readonly MetadataItem _metadata; /// /// Base directory path where email item directories are located /// private readonly string _basePath; /// /// File system abstraction for testable file operations /// private readonly IFileSystem _fileSystem; #endregion #region Constructor /// /// Initializes a new instance of the EmailItem class /// /// The metadata item containing email information /// Base directory path for file operations /// File system abstraction for file operations /// Thrown when metadata, basePath, or fileSystem is null public EmailItem(MetadataItem metadata, string basePath, IFileSystem fileSystem) { _metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); _basePath = basePath ?? throw new ArgumentNullException(nameof(basePath)); _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); } #endregion #region Public Properties /// /// Gets the underlying metadata item /// public MetadataItem Metadata => _metadata; /// /// Gets the full directory path for this email item /// Combines the base path with the directory name from metadata /// public string DirectoryPath => _fileSystem.Path.Combine(_basePath, _metadata.DirectoryName); /// /// Gets a value indicating whether the directory exists on the file system /// public bool DirectoryExists => _fileSystem.Directory.Exists(DirectoryPath); /// /// Gets all JPG files in the email item's directory /// Returns an empty enumerable if the directory doesn't exist /// public IEnumerable JpgFiles => DirectoryExists ? _fileSystem.Directory.GetFiles(DirectoryPath, "*.jpg") : Enumerable.Empty(); /// /// Gets the count of JPG files in the directory /// public int FileCount => JpgFiles.Count(); /// /// Gets the total size in bytes of all JPG files in the directory /// public long TotalFileSize => JpgFiles.Sum(f => _fileSystem.FileInfo.New(f).Length); /// /// Gets a value indicating whether this item is ready for processing /// Checks that the state is Ready, directory exists, and contains files /// public bool IsReadyForProcessing => _metadata.State == EmailStateEnum.Ready && DirectoryExists && FileCount > 0; /// /// Gets a value indicating whether this item can be reset /// Items cannot be reset while they are being processed or uploaded /// public bool CanBeReset => _metadata.State != EmailStateEnum.Processing && _metadata.State != EmailStateEnum.Uploading; /// /// Gets the normalized filename for the recipient (safe for file system) /// Replaces @ with _at_ and . with _dot_ to create filesystem-safe names /// public string NormalizedRecipient => _metadata.EmailAddress.Replace("@", "_at_").Replace(".", "_dot_"); #endregion #region Public Methods /// /// Generates the zip filename for this item based on recipient and timestamp /// /// Optional timestamp to use, defaults to current time /// A formatted zip filename in the format: studioufoto_{recipient}_{timestamp}.zip public string GenerateZipFileName(DateTime? timestamp = null) => $"studioufoto_{NormalizedRecipient}_{(timestamp ?? DateTime.Now):yyyyMMdd_HHmmss}.zip"; /// /// Validates that the item is in a consistent state for processing /// /// A tuple containing validation result and list of issues found public (bool IsValid, List Issues) Validate() { var issues = new List(); // Check required email address if (string.IsNullOrEmpty(_metadata.EmailAddress)) issues.Add("Email address is required"); // Check directory and files existence if (!DirectoryExists) issues.Add($"Directory does not exist: {DirectoryPath}"); else if (FileCount == 0) issues.Add("No JPG files found in directory"); // Check required email content if (string.IsNullOrEmpty(_metadata.Subject)) issues.Add("Email subject is required"); if (string.IsNullOrEmpty(_metadata.Body)) issues.Add("Email body is required"); return (issues.Count == 0, issues); } /// /// Gets a summary of the item for display purposes /// Includes recipient, file count, current state, and associated orders /// /// A formatted summary string public string GetSummary() => $"{_metadata.EmailAddress} - {FileCount} files, {_metadata.State}, Orders: {_metadata.Orders}"; /// /// Gets detailed file information for all JPG files in the directory /// /// Collection of file information objects public IEnumerable GetFileDetails() => JpgFiles.Select(f => _fileSystem.FileInfo.New(f)); /// /// Checks if a specific file exists in the email item's directory /// /// Name of the file to check /// True if the file exists, false otherwise public bool FileExists(string fileName) { if (string.IsNullOrEmpty(fileName)) return false; var filePath = _fileSystem.Path.Combine(DirectoryPath, fileName); return _fileSystem.File.Exists(filePath); } /// /// Gets the full path for a file within the email item's directory /// /// Name of the file /// Full path to the file public string GetFilePath(string fileName) => _fileSystem.Path.Combine(DirectoryPath, fileName); /// /// Creates the email item's directory if it doesn't exist /// /// True if directory was created or already exists, false if creation failed public bool EnsureDirectoryExists() { try { if (!DirectoryExists) { _fileSystem.Directory.CreateDirectory(DirectoryPath); } return true; } catch { return false; } } /// /// Gets file information for a specific file in the directory /// /// Name of the file /// File information object, or null if file doesn't exist public IFileInfo? GetFileInfo(string fileName) { if (string.IsNullOrEmpty(fileName)) return null; var filePath = GetFilePath(fileName); return _fileSystem.File.Exists(filePath) ? _fileSystem.FileInfo.New(filePath) : null; } #endregion } }