using Femto.Modules.Media.Data;

namespace Femto.Modules.Media.Infrastructure;

internal class FilesystemStorageProvider : IStorageProvider
{
    private readonly string _storageRoot;

    public FilesystemStorageProvider(string storageRoot)
    {
        if (string.IsNullOrWhiteSpace(storageRoot))
            throw new ArgumentException(
                "Storage root cannot be null or empty.",
                nameof(storageRoot)
            );

        this._storageRoot = Path.GetFullPath(storageRoot);

        if (!Directory.Exists(this._storageRoot))
        {
            Directory.CreateDirectory(this._storageRoot);
        }
    }

    public async Task SaveBlob(string id, Stream data)
    {
        string filePath = this.GetSafeFilePath(id);

        // Ensure the directory exists
        Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);

        await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
        await data.CopyToAsync(fileStream);
    }

    public async Task<Stream> LoadBlob(string id)
    {
        string filePath = this.GetSafeFilePath(id);

        if (!File.Exists(filePath))
            throw new FileNotFoundException("The blob with the specified ID was not found.", id);

        Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);

        return fileStream;
    }

    private string GetSafeFilePath(string id)
    {
        if (string.IsNullOrWhiteSpace(id))
            throw new ArgumentException("Blob ID cannot be null or empty.", nameof(id));

        // Sanitize and validate the path
        string combinedPath = Path.Combine(this._storageRoot, id);
        string fullPath = Path.GetFullPath(combinedPath);

        if (!fullPath.StartsWith(this._storageRoot, StringComparison.Ordinal))
            throw new UnauthorizedAccessException("Access to the path is denied.");

        return fullPath;
    }
}
