using Microsoft.AspNetCore.Hosting;
using NexusReader.Application.Abstractions.Services;

namespace NexusReader.Infrastructure.Services;

/// <summary>
/// Infrastructure implementation of general storage utilizing local filesystem.
/// Files are saved in wwwroot/uploads/media.
/// </summary>
public class LocalStorageService : IStorageService
{
    private readonly IWebHostEnvironment _environment;

    public LocalStorageService(IWebHostEnvironment environment)
    {
        _environment = environment;
    }

    public async Task<string> UploadFileAsync(byte[] fileBytes, string fileName, string contentType)
    {
        using var stream = new MemoryStream(fileBytes);
        return await UploadFileAsync(stream, fileName, contentType);
    }

    public async Task<string> UploadFileAsync(Stream fileStream, string fileName, string contentType)
    {
        var mediaFolder = Path.Combine(_environment.WebRootPath, "uploads");
        var resolvedMediaFolder = Path.GetFullPath(mediaFolder);
        var folderWithSeparator = resolvedMediaFolder.EndsWith(Path.DirectorySeparatorChar)
            ? resolvedMediaFolder
            : resolvedMediaFolder + Path.DirectorySeparatorChar;
        
        if (!Directory.Exists(resolvedMediaFolder))
        {
            Directory.CreateDirectory(resolvedMediaFolder);
        }

        // Clean file name to prevent path traversal issues
        var safeFileName = Path.GetFileName(fileName);
        var uniqueFileName = $"{Guid.NewGuid()}_{safeFileName}";
        var filePath = Path.Combine(resolvedMediaFolder, uniqueFileName);
        
        // Guard against path traversal
        var fullPath = Path.GetFullPath(filePath);
        if (!fullPath.StartsWith(folderWithSeparator, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Path traversal detected.");
        }

        using (var outputStream = new FileStream(fullPath, FileMode.Create))
        {
            await fileStream.CopyToAsync(outputStream);
        }

        // Return the public web-relative URL
        return $"/uploads/{uniqueFileName}";
    }
}
