namespace Books.Api.Infrastructure.FileStorage;

/// <summary>
/// Local file system storage implementation.
/// Suitable for development and small deployments.
/// For production, consider Azure Blob Storage or AWS S3.
/// </summary>
public class LocalFileStorageService : IFileStorageService
{
    private readonly string _basePath;
    private readonly string _baseUrl;

    public LocalFileStorageService(IConfiguration configuration)
    {
        _basePath = configuration["FileStorage:LocalPath"]
            ?? Path.Combine(Directory.GetCurrentDirectory(), "uploads");
        _baseUrl = configuration["FileStorage:BaseUrl"]
            ?? "/api/attachments";

        // Ensure base directory exists
        if (!Directory.Exists(_basePath))
        {
            Directory.CreateDirectory(_basePath);
        }
    }

    public async Task<StorageResult> StoreAsync(
        string companyId,
        string fileName,
        string contentType,
        Stream content,
        CancellationToken cancellationToken = default)
    {
        // Create company directory
        var companyPath = Path.Combine(_basePath, companyId);
        if (!Directory.Exists(companyPath))
        {
            Directory.CreateDirectory(companyPath);
        }

        // Generate unique filename to prevent collisions
        var extension = Path.GetExtension(fileName);
        var sanitizedName = SanitizeFileName(Path.GetFileNameWithoutExtension(fileName));
        var uniqueId = Guid.NewGuid().ToString("N")[..8];
        var storedFileName = $"{sanitizedName}_{uniqueId}{extension}";

        var filePath = Path.Combine(companyPath, storedFileName);

        // Store file
        await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
        await content.CopyToAsync(fileStream, cancellationToken);

        // Storage path is relative to base (company/filename)
        var storagePath = $"{companyId}/{storedFileName}";

        return new StorageResult
        {
            StoragePath = storagePath,
            StoredFileName = storedFileName,
            FileSize = fileStream.Length
        };
    }

    public Task<FileResult> GetAsync(string storagePath, CancellationToken cancellationToken = default)
    {
        var filePath = Path.Combine(_basePath, storagePath);

        if (!File.Exists(filePath))
            return Task.FromResult<FileResult?>(null)!;

        var fileInfo = new FileInfo(filePath);
        var contentType = GetContentType(fileInfo.Extension);

        // Open for reading (caller is responsible for disposing)
        var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

        return Task.FromResult(new FileResult
        {
            Content = stream,
            ContentType = contentType,
            FileName = fileInfo.Name,
            FileSize = fileInfo.Length
        });
    }

    public Task DeleteAsync(string storagePath, CancellationToken cancellationToken = default)
    {
        var filePath = Path.Combine(_basePath, storagePath);

        if (File.Exists(filePath))
        {
            File.Delete(filePath);
        }

        return Task.CompletedTask;
    }

    public string GetDownloadUrl(string storagePath, TimeSpan? expiresIn = null)
    {
        // For local storage, return API endpoint URL
        // The actual download is handled by a controller
        return $"{_baseUrl}/{Uri.EscapeDataString(storagePath)}";
    }

    private static string SanitizeFileName(string fileName)
    {
        // Remove invalid characters
        var invalid = Path.GetInvalidFileNameChars();
        var sanitized = string.Join("", fileName.Split(invalid));

        // Limit length
        if (sanitized.Length > 50)
            sanitized = sanitized[..50];

        // Default if empty
        if (string.IsNullOrWhiteSpace(sanitized))
            sanitized = "attachment";

        return sanitized;
    }

    private static string GetContentType(string extension)
    {
        return extension.ToLowerInvariant() switch
        {
            ".pdf" => "application/pdf",
            ".png" => "image/png",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".gif" => "image/gif",
            ".webp" => "image/webp",
            ".doc" => "application/msword",
            ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            ".xls" => "application/vnd.ms-excel",
            ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            _ => "application/octet-stream"
        };
    }
}
