using Aliyun.OSS;
using Microsoft.Extensions.Options;
using TakeoutSaaS.Module.Storage.Abstractions;
using TakeoutSaaS.Module.Storage.Models;
using TakeoutSaaS.Module.Storage.Options;

namespace TakeoutSaaS.Module.Storage.Providers;

/// <summary>
/// 阿里云 OSS 存储提供商实现。
/// </summary>
public sealed class AliyunOssStorageProvider(IOptionsMonitor<StorageOptions> optionsMonitor) : IObjectStorageProvider, IDisposable
{
    private OssClient? _client;
    private bool _disposed;

    private StorageOptions CurrentOptions => optionsMonitor.CurrentValue;

    /// <inheritdoc />
    public StorageProviderKind Kind => StorageProviderKind.AliyunOss;

    /// <inheritdoc />
    public async Task<StorageUploadResult> UploadAsync(StorageUploadRequest request, CancellationToken cancellationToken = default)
    {
        // 1. 准备元数据
        var options = CurrentOptions;
        var metadata = new ObjectMetadata
        {
            ContentLength = request.ContentLength,
            ContentType = request.ContentType
        };

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

        // Aliyun OSS SDK 支持异步方法，如未支持将同步封装为任务。
        // 2. 上传对象
        await PutObjectAsync(options.AliyunOss.Bucket, request.ObjectKey, request.Content, metadata, cancellationToken)
            .ConfigureAwait(false);

        // 3. 生成签名或公有 URL
        var signedUrl = request.GenerateSignedUrl
            ? await GenerateDownloadUrlAsync(request.ObjectKey, request.SignedUrlExpires, cancellationToken).ConfigureAwait(false)
            : null;

        // 4. 返回上传结果
        return new StorageUploadResult
        {
            ObjectKey = request.ObjectKey,
            Url = signedUrl ?? BuildPublicUrl(request.ObjectKey),
            SignedUrl = signedUrl,
            FileSize = request.ContentLength,
            ContentType = request.ContentType
        };
    }

    /// <inheritdoc />
    public Task<StorageDirectUploadResult> CreateDirectUploadAsync(StorageDirectUploadRequest request, CancellationToken cancellationToken = default)
    {
        // 1. 计算过期时间并生成直传/下载链接
        var expiresAt = DateTimeOffset.UtcNow.Add(request.Expires);
        var uploadUrl = GeneratePresignedUrl(request.ObjectKey, request.Expires, SignHttpMethod.Put, request.ContentType);
        var downloadUrl = GeneratePresignedUrl(request.ObjectKey, request.Expires, SignHttpMethod.Get, null);

        // 2. 返回直传参数
        var result = new StorageDirectUploadResult
        {
            UploadUrl = uploadUrl,
            FormFields = new Dictionary<string, string>(),
            ExpiresAt = expiresAt,
            ObjectKey = request.ObjectKey,
            SignedDownloadUrl = downloadUrl
        };

        return Task.FromResult(result);
    }

    /// <inheritdoc />
    public Task<string> GenerateDownloadUrlAsync(string objectKey, TimeSpan expires, CancellationToken cancellationToken = default)
    {
        // 1. 生成预签名下载 URL
        var url = GeneratePresignedUrl(objectKey, expires, SignHttpMethod.Get, null);
        return Task.FromResult(url);
    }

    /// <inheritdoc />
    public string BuildPublicUrl(string objectKey)
    {
        var cdn = CurrentOptions.AliyunOss.CdnBaseUrl ?? CurrentOptions.CdnBaseUrl;
        if (!string.IsNullOrWhiteSpace(cdn))
        {
            return $"{cdn!.TrimEnd('/')}/{objectKey}";
        }

        var endpoint = CurrentOptions.AliyunOss.Endpoint.TrimEnd('/');
        var scheme = CurrentOptions.AliyunOss.UseHttps ? "https" : "http";
        // Endpoint 可能已包含协议，若没有则补充。
        if (!endpoint.StartsWith("http", StringComparison.OrdinalIgnoreCase))
        {
            endpoint = $"{scheme}://{endpoint}";
        }

        return $"{endpoint}/{CurrentOptions.AliyunOss.Bucket}/{objectKey}";
    }

    /// <summary>
    /// 上传对象到 OSS。
    /// </summary>
    private async Task PutObjectAsync(string bucket, string key, Stream content, ObjectMetadata metadata, CancellationToken cancellationToken)
    {
        var client = EnsureClient();
        // SDK 无异步则封装为 Task
        await Task.Run(() => client.PutObject(bucket, key, content, metadata), cancellationToken).ConfigureAwait(false);
    }

    /// <summary>
    /// 生成预签名 URL。
    /// </summary>
    private string GeneratePresignedUrl(string objectKey, TimeSpan expires, SignHttpMethod method, string? contentType)
    {
        var request = new GeneratePresignedUriRequest(CurrentOptions.AliyunOss.Bucket, objectKey, method)
        {
            Expiration = DateTime.Now.Add(expires)
        };

        if (!string.IsNullOrWhiteSpace(contentType))
        {
            request.ContentType = contentType;
        }

        var uri = EnsureClient().GeneratePresignedUri(request);
        return uri.ToString();
    }

    /// <summary>
    /// 构建或复用 OSS 客户端。
    /// </summary>
    private OssClient EnsureClient()
    {
        if (_client != null)
        {
            return _client;
        }

        var options = CurrentOptions.AliyunOss;
        _client = new OssClient(options.Endpoint, options.AccessKeyId, options.AccessKeySecret);
        return _client;
    }

    /// <inheritdoc />
    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        _disposed = true;
    }
}
