namespace Fast.Logic.FileService;

/// Unified interface for file storage operations supporting both local filesystem and cloud storage backends.
/// All operations can work in sharded mode (files distributed across calculated subfolders) or non-sharded mode (flat structure).
public interface IFileService
{
    Task<bool> FileExistsAsync(string folderName, string fileName, bool useSharding = true);
    bool FileExists(string folderName, string fileName, bool useSharding = true);

    Task<byte[]> ReadAllBytesAsync(string folderName, string fileName, bool useSharding = true);
    byte[] ReadAllBytes(string folderName, string fileName, bool useSharding = true);

    Task<string> WriteAllBytesAsync(string folderName, string fileName, byte[] content, bool useSharding = true);
    string WriteAllBytes(string folderName, string fileName, byte[] content, bool useSharding = true);

    Task<bool> DeleteFileAsync(string folderName, string fileName, bool useSharding = true);
    bool DeleteFile(string folderName, string fileName, bool useSharding = true);

    Task CopyFileAsync(string sourceFolderName, string sourceFileName, string destFolderName, string destFileName, bool useSharding = true, bool overwrite = false);

    Task MoveFileAsync(string sourceFolderName, string sourceFileName, string destFolderName, string destFileName, bool useSharding = true, bool overwrite = false);

    Task<Stream> OpenReadAsync(string folderName, string fileName, bool useSharding = true);

    Task<string> WriteStreamAsync(string folderName, string fileName, Stream content, bool useSharding = true);

    Task<string?> UploadFileAsync(string folderName, string fileName, IFormFile file, bool useSharding = true);

    Task<FileDownloadResult> DownloadFileAsync(string folderName, string fileName, bool useSharding = true);

    Task<bool> DirectoryExistsAsync(string folderName, string? subFolder = null);
    bool DirectoryExists(string folderName, string? subFolder = null);

    Task<bool> CreateDirectoryAsync(string folderName, string? subFolder = null);
    bool CreateDirectory(string folderName, string? subFolder = null);

    Task<bool> DeleteDirectoryAsync(string folderName, string? subFolder = null, bool recursive = false);
    bool DeleteDirectory(string folderName, string? subFolder = null, bool recursive = false);

    Task<IReadOnlyList<string>> GetFilesAsync(string folderName, string? subFolder = null, string searchPattern = "*", bool recursive = false);

    Task<IReadOnlyList<FileItemInfo>> GetFilesWithInfoAsync(string folderName, string? subFolder = null, string searchPattern = "*", bool recursive = false);

    Task<IReadOnlyList<string>> GetDirectoriesAsync(string folderName, string? subFolder = null);

    /// Writes multiple files in a single batch operation. Optimized for cloud storage backends.
    /// <param name="files">Collection of files to write, each with folder, filename, and content.</param>
    /// <param name="useSharding">When true, calculates sharded subfolder paths for each file.</param>
    /// <param name="maxDegreeOfParallelism">Maximum concurrent operations. Default is 4.</param>
    Task<IReadOnlyList<BatchOperationResult>> WriteAllFilesBatchedAsync(
        IEnumerable<(string folderName, string fileName, byte[] content)> files,
        bool useSharding = true,
        int maxDegreeOfParallelism = 4);

    /// Deletes multiple files in a single batch operation. Optimized for cloud storage backends.
    /// <param name="files">Collection of files to delete, each with folder and filename.</param>
    /// <param name="useSharding">When true, calculates sharded subfolder paths for each file.</param>
    /// <param name="maxDegreeOfParallelism">Maximum concurrent operations. Default is 4.</param>
    Task<IReadOnlyList<BatchOperationResult>> DeleteFilesBatchedAsync(
        IEnumerable<(string folderName, string fileName)> files,
        bool useSharding = true,
        int maxDegreeOfParallelism = 4);

    Task<FileItemInfo?> GetFileInfoAsync(string folderName, string fileName, bool useSharding = true);

    /// Gets the calculated shard folder name for a given file name.
    /// <param name="fileName">The file name to calculate sharding for.</param>
    /// <returns>The shard folder name (e.g., "ab" for GUID files or "1000-1999" for ticket files).</returns>
    string GetShardFolderName(string fileName);

    /// Resolves the full path for a file, including sharding if applicable.
    /// <param name="folderName">The logical folder name.</param>
    /// <param name="fileName">The file name.</param>
    /// <param name="useSharding">When true, includes the sharded subfolder in the path.</param>
    /// <returns>The resolved path (format depends on implementation).</returns>
    string ResolvePath(string folderName, string fileName, bool useSharding = true);
}

public class FileDownloadResult : IDisposable
{
    /// The file content stream. Caller is responsible for disposing.
    public Stream Stream { get; set; } = Stream.Null;

    /// The MIME content type of the file.
    public string ContentType { get; set; } = "application/octet-stream";

    public string FileName { get; set; } = string.Empty;

    /// The file size in bytes, if available.
    public long? Size { get; set; }

    public DateTime? LastModified { get; set; }

    public bool Success { get; set; } = true;

    public string? ErrorMessage { get; set; }

    public void Dispose()
    {
        Stream?.Dispose();
    }
}

/// Information about a file in storage.
public class FileItemInfo
{
    /// The file name without path.
    public string Name { get; set; } = string.Empty;

    /// The full path or identifier for the file.
    public string FullPath { get; set; } = string.Empty;

    /// The file size in bytes.
    public long Size { get; set; }

    public DateTime? CreatedDate { get; set; }
    public DateTime? LastModifiedDate { get; set; }

    /// The MIME content type, if available.
    public string? ContentType { get; set; }

    /// A URL to access the file directly (primarily for SharePoint).
    public string? WebUrl { get; set; }

    /// A unique identifier for the file (primarily for SharePoint DriveItem ID).
    public string? Id { get; set; }
}

public class BatchOperationResult
{
    public string FolderName { get; set; } = string.Empty;
    public string FileName { get; set; } = string.Empty;
    public bool Success { get; set; }
    public string? ErrorMessage { get; set; }
    public Exception? Exception { get; set; }
}