using Microsoft.Extensions.Options;
using Tiku.Application.Storage;
using Tiku.Infrastructure.Storage;

namespace Tiku.UnitTests.Storage;

public sealed class AliyunOssObjectStorageServiceTests
{
    private static readonly Guid TenantId = Guid.Parse("11111111-1111-1111-1111-111111111111");

    [Fact]
    public void Validate_object_key_requires_tenant_prefix_like_original_storage_service()
    {
        using var service = CreateConfiguredService();

        var exception = Assert.Throws<ObjectStorageException>(() =>
            service.ValidateObjectKey(TenantId, "other-tenant/file.pdf"));

        Assert.Equal("OBJECT_KEY_TENANT_PREFIX_REQUIRED", exception.Code);
    }

    [Fact]
    public void Validate_mime_type_and_size_use_configured_allow_list()
    {
        using var service = CreateConfiguredService(new ObjectStorageOptions
        {
            MaxUploadBytes = 100,
            AllowedMimePrefixes = ["image/"],
            AllowedMimeTypes = ["application/pdf"]
        });

        Assert.Equal("image/png", service.ValidateMimeType(" Image/PNG "));
        Assert.Equal(99, service.ValidateFileSize(99));
        Assert.Equal("MIME_TYPE_NOT_ALLOWED",
            Assert.Throws<ObjectStorageException>(() => service.ValidateMimeType("text/html")).Code);
        Assert.Equal("FILE_TOO_LARGE", Assert.Throws<ObjectStorageException>(() => service.ValidateFileSize(101)).Code);
    }

    [Fact]
    public async Task Sign_download_requires_complete_aliyun_oss_options()
    {
        using var service = CreateUnconfiguredService();

        var exception = await Assert.ThrowsAsync<ObjectStorageNotConfiguredException>(() =>
            service.SignDownloadAsync(new ObjectStorageDownloadSignRequest(
                TenantId,
                ObjectStorageProviders.AliyunOss,
                "bucket",
                $"{TenantId:N}/path/file.pdf",
                TimeSpan.FromMinutes(15))));

        Assert.Contains("ALIYUN_OSS_ACCESS_KEY_ID", exception.Message, StringComparison.Ordinal);
    }

    [Fact]
    public async Task Sign_download_generates_aliyun_oss_presigned_get_url()
    {
        using var service = CreateConfiguredService();

        var signedUrl = await service.SignDownloadAsync(new ObjectStorageDownloadSignRequest(
            TenantId,
            ObjectStorageProviders.AliyunOss,
            "tiku-assets",
            $"{TenantId:N}/handbook.pdf",
            TimeSpan.FromMinutes(15),
            FileName: "handbook.pdf",
            Disposition: "attachment"));

        Assert.Equal(ObjectStorageProviders.AliyunOss, signedUrl.Provider);
        Assert.Equal("GET", signedUrl.Method);
        Assert.Equal("aliyun-oss-signature-url-v4", signedUrl.SignatureMode);
        Assert.Contains("tiku-assets", signedUrl.Url.Host, StringComparison.Ordinal);
        Assert.Contains($"{TenantId:N}/handbook.pdf", signedUrl.Url.AbsoluteUri, StringComparison.Ordinal);
        Assert.Contains("x-oss-", signedUrl.Url.Query, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public async Task Sign_upload_generates_aliyun_oss_presigned_put_url_with_content_type_header()
    {
        using var service = CreateConfiguredService();

        var signedUrl = await service.SignUploadAsync(new ObjectStorageUploadSignRequest(
            TenantId,
            ObjectStorageProviders.AliyunOss,
            "tiku-assets",
            $"{TenantId:N}/upload.pdf",
            "upload.pdf",
            "application/pdf",
            1024,
            TimeSpan.FromMinutes(10)));

        Assert.Equal("PUT", signedUrl.Method);
        Assert.Contains("x-oss-", signedUrl.Url.Query, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public async Task Cdn_url_download_returns_public_or_provider_managed_url_without_oss_signing()
    {
        using var service = CreateUnconfiguredService();

        var signedUrl = await service.SignDownloadAsync(new ObjectStorageDownloadSignRequest(
            TenantId,
            ObjectStorageProviders.ExternalUrl,
            null,
            null,
            TimeSpan.FromMinutes(5),
            "https://cdn.example.com/file.pdf"));

        Assert.Equal("public-or-provider-managed", signedUrl.SignatureMode);
        Assert.Equal("https://cdn.example.com/file.pdf", signedUrl.Url.AbsoluteUri);
    }

    [Fact]
    public void Aliyun_oss_options_allow_empty_optional_configuration()
    {
        Assert.True(AliyunOssOptions.BeValid(new AliyunOssOptions()));
        Assert.False(AliyunOssOptions.BeValid(new AliyunOssOptions
        {
            PresignDefaultMinutes = 0
        }));
    }

    private static AliyunOssObjectStorageService CreateConfiguredService(ObjectStorageOptions? storageOptions = null)
    {
        return new AliyunOssObjectStorageService(
            Options.Create(storageOptions ?? new ObjectStorageOptions()),
            Options.Create(new AliyunOssOptions
            {
                Region = "cn-hangzhou",
                AccessKeyId = "test-access-key-id",
                AccessKeySecret = "test-access-key-secret"
            }));
    }

    private static AliyunOssObjectStorageService CreateUnconfiguredService()
    {
        return new AliyunOssObjectStorageService(Options.Create(new ObjectStorageOptions()),
            Options.Create(new AliyunOssOptions()));
    }
}