using Aliyun.OSS;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StorageService.Domain.AggregatesModel.FileAggregate;
using StorageService.Infrastructure.Configuration;

namespace StorageService.Infrastructure.Storage;

/// <summary>
/// EN: Aliyun OSS storage provider implementation.
/// VI: Implementation storage provider cho Aliyun OSS.
/// </summary>
public class AliyunOssStorageProvider : IStorageProvider
{
    private readonly IOss _ossClient;
    private readonly ILogger<AliyunOssStorageProvider> _logger;
    private readonly AliyunOssSettings _settings;

    public StorageProvider ProviderType => StorageProvider.AliyunOSS;

    public AliyunOssStorageProvider(
        IOptions<StorageSettings> settings,
        ILogger<AliyunOssStorageProvider> logger)
    {
        _logger = logger;
        _settings = settings.Value.AliyunOSS;

        _ossClient = new OssClient(
            _settings.Endpoint,
            _settings.AccessKeyId,
            _settings.AccessKeySecret);

        _logger.LogInformation("Aliyun OSS client initialized with endpoint: {Endpoint}", _settings.Endpoint);
    }

    /// <inheritdoc />
    public Task<StorageResult> UploadAsync(
        string bucketName,
        string objectKey,
        Stream dataStream,
        string contentType,
        CancellationToken cancellationToken = default)
    {
        try
        {
            EnsureBucketExistsAsync(bucketName, cancellationToken).Wait();

            var metadata = new ObjectMetadata { ContentType = contentType };
            var result = _ossClient.PutObject(bucketName, objectKey, dataStream, metadata);
            
            _logger.LogInformation("File uploaded to Aliyun OSS: {Bucket}/{ObjectKey}", bucketName, objectKey);
            
            return Task.FromResult(StorageResult.Ok(objectKey, dataStream.Length, result.ETag));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to upload file to Aliyun OSS: {Bucket}/{ObjectKey}", bucketName, objectKey);
            return Task.FromResult(StorageResult.Fail(ex.Message));
        }
    }

    /// <inheritdoc />
    public Task<Stream?> DownloadAsync(
        string bucketName,
        string objectKey,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var ossObject = _ossClient.GetObject(bucketName, objectKey);
            var memoryStream = new MemoryStream();
            ossObject.Content.CopyTo(memoryStream);
            memoryStream.Position = 0;
            
            return Task.FromResult<Stream?>(memoryStream);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to download file from Aliyun OSS: {Bucket}/{ObjectKey}", bucketName, objectKey);
            return Task.FromResult<Stream?>(null);
        }
    }

    /// <inheritdoc />
    public Task<bool> DeleteAsync(
        string bucketName,
        string objectKey,
        CancellationToken cancellationToken = default)
    {
        try
        {
            _ossClient.DeleteObject(bucketName, objectKey);
            _logger.LogInformation("File deleted from Aliyun OSS: {Bucket}/{ObjectKey}", bucketName, objectKey);
            return Task.FromResult(true);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to delete file from Aliyun OSS: {Bucket}/{ObjectKey}", bucketName, objectKey);
            return Task.FromResult(false);
        }
    }

    /// <inheritdoc />
    public Task<bool> ExistsAsync(
        string bucketName,
        string objectKey,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var exists = _ossClient.DoesObjectExist(bucketName, objectKey);
            return Task.FromResult(exists);
        }
        catch
        {
            return Task.FromResult(false);
        }
    }

    /// <inheritdoc />
    public Task<string> GetPreSignedDownloadUrlAsync(
        string bucketName,
        string objectKey,
        int expirationSeconds = 3600,
        CancellationToken cancellationToken = default)
    {
        var expiration = DateTime.UtcNow.AddSeconds(expirationSeconds);
        var uri = _ossClient.GeneratePresignedUri(bucketName, objectKey, expiration, SignHttpMethod.Get);
        return Task.FromResult(uri.ToString());
    }

    /// <inheritdoc />
    public Task<string> GetPreSignedUploadUrlAsync(
        string bucketName,
        string objectKey,
        int expirationSeconds = 3600,
        CancellationToken cancellationToken = default)
    {
        var expiration = DateTime.UtcNow.AddSeconds(expirationSeconds);
        var uri = _ossClient.GeneratePresignedUri(bucketName, objectKey, expiration, SignHttpMethod.Put);
        return Task.FromResult(uri.ToString());
    }

    /// <inheritdoc />
    public Task EnsureBucketExistsAsync(
        string bucketName,
        CancellationToken cancellationToken = default)
    {
        try
        {
            if (!_ossClient.DoesBucketExist(bucketName))
            {
                _ossClient.CreateBucket(bucketName);
                _logger.LogInformation("Created Aliyun OSS bucket: {Bucket}", bucketName);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to ensure bucket exists: {Bucket}", bucketName);
        }
        
        return Task.CompletedTask;
    }

    // ========== Multipart Upload Methods ==========

    /// <inheritdoc />
    public Task<string> InitiateMultipartUploadAsync(
        string bucketName,
        string objectKey,
        string contentType,
        CancellationToken cancellationToken = default)
    {
        // TODO: Implement using Aliyun OSS InitiateMultipartUpload
        _logger.LogWarning("Multipart upload for Aliyun OSS not yet implemented");
        throw new NotImplementedException("Multipart upload will be implemented in next phase");
    }

    /// <inheritdoc />
    public Task<string> UploadPartAsync(
        string bucketName,
        string objectKey,
        string uploadId,
        int partNumber,
        Stream dataStream,
        CancellationToken cancellationToken = default)
    {
        // TODO: Implement using Aliyun OSS UploadPart
        _logger.LogWarning("Upload part for Aliyun OSS not yet implemented");
        throw new NotImplementedException("Upload part will be implemented in next phase");
    }

    /// <inheritdoc />
    public Task CompleteMultipartUploadAsync(
        string bucketName,
        string objectKey,
        string uploadId,
        List<PartETag> parts,
        CancellationToken cancellationToken = default)
    {
        // TODO: Implement using Aliyun OSS CompleteMultipartUpload
        _logger.LogWarning("Complete multipart upload for Aliyun OSS not yet implemented");
        throw new NotImplementedException("Complete multipart upload will be implemented in next phase");
    }

    /// <inheritdoc />
    public Task AbortMultipartUploadAsync(
        string bucketName,
        string objectKey,
        string uploadId,
        CancellationToken cancellationToken = default)
    {
        // TODO: Implement using Aliyun OSS AbortMultipartUpload
        _logger.LogWarning("Abort multipart upload for Aliyun OSS not yet implemented");
        throw new NotImplementedException("Abort multipart upload will be implemented in next phase");
    }
}
