using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RealCV.Application.Interfaces;
using RealCV.Infrastructure.Configuration;

namespace RealCV.Infrastructure.Services;

public sealed class FileStorageService : IFileStorageService
{
    private readonly BlobContainerClient _containerClient;
    private readonly ILogger<FileStorageService> _logger;

    public FileStorageService(
        IOptions<AzureBlobSettings> settings,
        ILogger<FileStorageService> logger)
    {
        _logger = logger;

        var blobServiceClient = new BlobServiceClient(settings.Value.ConnectionString);
        _containerClient = blobServiceClient.GetBlobContainerClient(settings.Value.ContainerName);
    }

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

        var extension = Path.GetExtension(fileName);
        var uniqueBlobName = $"{Guid.NewGuid()}{extension}";

        _logger.LogDebug("Uploading file {FileName} as blob {BlobName}", fileName, uniqueBlobName);

        var blobClient = _containerClient.GetBlobClient(uniqueBlobName);

        await _containerClient.CreateIfNotExistsAsync();

        var httpHeaders = new BlobHttpHeaders
        {
            ContentType = GetContentType(extension)
        };

        await blobClient.UploadAsync(fileStream, new BlobUploadOptions
        {
            HttpHeaders = httpHeaders,
            Metadata = new Dictionary<string, string>
            {
                ["originalFileName"] = fileName,
                ["uploadedAt"] = DateTime.UtcNow.ToString("O")
            }
        });

        var blobUrl = blobClient.Uri.ToString();

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

        return blobUrl;
    }

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

        var blobName = ExtractBlobNameFromUrl(blobUrl);

        _logger.LogDebug("Downloading blob {BlobName} from {BlobUrl}", blobName, blobUrl);

        var blobClient = _containerClient.GetBlobClient(blobName);

        // Download to memory stream to ensure proper resource management
        // The caller will own and dispose this stream
        var memoryStream = new MemoryStream();
        await blobClient.DownloadToAsync(memoryStream);
        memoryStream.Position = 0;

        _logger.LogDebug("Successfully downloaded blob {BlobName}", blobName);

        return memoryStream;
    }

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

        var blobName = ExtractBlobNameFromUrl(blobUrl);

        _logger.LogDebug("Deleting blob {BlobName}", blobName);

        var blobClient = _containerClient.GetBlobClient(blobName);

        var deleted = await blobClient.DeleteIfExistsAsync();

        if (deleted)
        {
            _logger.LogInformation("Successfully deleted blob {BlobName}", blobName);
        }
        else
        {
            _logger.LogWarning("Blob {BlobName} did not exist when attempting to delete", blobName);
        }
    }

    private static string ExtractBlobNameFromUrl(string blobUrl)
    {
        if (!Uri.TryCreate(blobUrl, UriKind.Absolute, out var uri))
        {
            throw new ArgumentException($"Invalid blob URL format: '{blobUrl}'", nameof(blobUrl));
        }

        var segments = uri.Segments;

        // The blob name is the last segment after the container name
        // URL format: https://account.blob.core.windows.net/container/blobname
        if (segments.Length <= 2)
        {
            throw new ArgumentException($"Blob URL does not contain a valid blob name: '{blobUrl}'", nameof(blobUrl));
        }

        return segments[^1];
    }

    private static string GetContentType(string extension)
    {
        return extension.ToLowerInvariant() switch
        {
            ".pdf" => "application/pdf",
            ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            ".doc" => "application/msword",
            _ => "application/octet-stream"
        };
    }
}
