using Amazon.S3;
using Amazon.S3.Model;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Vexer.Export;

namespace StellaOps.Vexer.ArtifactStores.S3;

public sealed class S3ArtifactClientOptions
{
    public string Region { get; set; } = "us-east-1";

    public string? ServiceUrl { get; set; }
        = null;

    public bool ForcePathStyle { get; set; }
        = true;
}

public sealed class S3ArtifactClient : IS3ArtifactClient
{
    private readonly IAmazonS3 _s3;
    private readonly ILogger<S3ArtifactClient> _logger;

    public S3ArtifactClient(IAmazonS3 s3, ILogger<S3ArtifactClient> logger)
    {
        _s3 = s3 ?? throw new ArgumentNullException(nameof(s3));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task<bool> ObjectExistsAsync(string bucketName, string key, CancellationToken cancellationToken)
    {
        try
        {
            var metadata = await _s3.GetObjectMetadataAsync(bucketName, key, cancellationToken).ConfigureAwait(false);
            return metadata.HttpStatusCode == System.Net.HttpStatusCode.OK;
        }
        catch (AmazonS3Exception ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            return false;
        }
    }

    public async Task PutObjectAsync(string bucketName, string key, Stream content, IDictionary<string, string> metadata, CancellationToken cancellationToken)
    {
        var request = new PutObjectRequest
        {
            BucketName = bucketName,
            Key = key,
            InputStream = content,
            AutoCloseStream = false,
        };

        foreach (var kvp in metadata)
        {
            request.Metadata[kvp.Key] = kvp.Value;
        }

        await _s3.PutObjectAsync(request, cancellationToken).ConfigureAwait(false);
        _logger.LogDebug("Uploaded object {Bucket}/{Key}", bucketName, key);
    }

    public async Task<Stream?> GetObjectAsync(string bucketName, string key, CancellationToken cancellationToken)
    {
        try
        {
            var response = await _s3.GetObjectAsync(bucketName, key, cancellationToken).ConfigureAwait(false);
            var buffer = new MemoryStream();
            await response.ResponseStream.CopyToAsync(buffer, cancellationToken).ConfigureAwait(false);
            buffer.Position = 0;
            return buffer;
        }
        catch (AmazonS3Exception ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            _logger.LogDebug("Object {Bucket}/{Key} not found", bucketName, key);
            return null;
        }
    }

    public async Task DeleteObjectAsync(string bucketName, string key, CancellationToken cancellationToken)
    {
        await _s3.DeleteObjectAsync(bucketName, key, cancellationToken).ConfigureAwait(false);
        _logger.LogDebug("Deleted object {Bucket}/{Key}", bucketName, key);
    }
}
