using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RealCV.Application.Interfaces;
using RealCV.Infrastructure.Configuration;

namespace RealCV.Infrastructure.Services;

public sealed class LocalFileStorageService : IFileStorageService
{
    private readonly string _storagePath;
    private readonly ILogger<LocalFileStorageService> _logger;

    public LocalFileStorageService(
        IOptions<LocalStorageSettings> settings,
        ILogger<LocalFileStorageService> logger)
    {
        _logger = logger;
        _storagePath = settings.Value.StoragePath;

        if (!Directory.Exists(_storagePath))
        {
            Directory.CreateDirectory(_storagePath);
            _logger.LogInformation("Created local storage directory: {Path}", _storagePath);
        }
    }

    public async Task<string> UploadAsync(Stream fileStream, string fileName)
    {
        ArgumentNullException.ThrowIfNull(fileStream);
        ArgumentException.ThrowIfNullOrWhiteSpace(fileName);

        var extension = Path.GetExtension(fileName);
        var uniqueFileName = $"{Guid.NewGuid()}{extension}";
        var filePath = Path.Combine(_storagePath, uniqueFileName);

        _logger.LogDebug("Uploading file {FileName} to {FilePath}", fileName, filePath);

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

        // Return a file:// URL for local storage
        var fileUrl = $"file://{filePath}";

        _logger.LogInformation("Successfully uploaded file {FileName} to {FileUrl}", fileName, fileUrl);

        return fileUrl;
    }

    public async Task<Stream> DownloadAsync(string blobUrl)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(blobUrl);

        var filePath = ExtractFilePathFromUrl(blobUrl);

        _logger.LogDebug("Downloading file from {FilePath}", filePath);

        if (!File.Exists(filePath))
        {
            throw new FileNotFoundException($"File not found: {filePath}");
        }

        var memoryStream = new MemoryStream();
        await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        await fileStream.CopyToAsync(memoryStream);
        memoryStream.Position = 0;

        _logger.LogDebug("Successfully downloaded file from {FilePath}", filePath);

        return memoryStream;
    }

    public Task DeleteAsync(string blobUrl)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(blobUrl);

        var filePath = ExtractFilePathFromUrl(blobUrl);

        _logger.LogDebug("Deleting file {FilePath}", filePath);

        if (File.Exists(filePath))
        {
            File.Delete(filePath);
            _logger.LogInformation("Successfully deleted file {FilePath}", filePath);
        }
        else
        {
            _logger.LogWarning("File {FilePath} did not exist when attempting to delete", filePath);
        }

        return Task.CompletedTask;
    }

    private string ExtractFilePathFromUrl(string fileUrl)
    {
        string filePath;

        if (fileUrl.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
        {
            filePath = fileUrl[7..];
        }
        else
        {
            filePath = fileUrl;
        }

        // Resolve to absolute path and validate it's within storage directory
        var fullPath = Path.GetFullPath(filePath);
        var storagePath = Path.GetFullPath(_storagePath);

        if (!fullPath.StartsWith(storagePath, StringComparison.OrdinalIgnoreCase))
        {
            throw new UnauthorizedAccessException($"Access denied: path is outside storage directory");
        }

        return fullPath;
    }
}
