using FsCheck;
using FsCheck.Xunit;
using HoneyBox.Admin.Business.Models.Config;
using HoneyBox.Admin.Business.Services.Storage;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace HoneyBox.Tests.Services;

/// <summary>
/// TencentCosProvider 单元测试
/// 注意: 由于COS SDK依赖，UploadAsync和DeleteAsync方法的测试需要在集成测试中进行
/// 这里只测试静态辅助方法和URL生成逻辑
/// </summary>
public class TencentCosProviderTests
{
    #region URL Generation Tests

    [Theory]
    [InlineData("https://cdn.example.com", "uploads/2026/01/19/test.jpg", "https://cdn.example.com/uploads/2026/01/19/test.jpg")]
    [InlineData("https://cdn.example.com/", "uploads/2026/01/19/test.jpg", "https://cdn.example.com/uploads/2026/01/19/test.jpg")]
    [InlineData("cdn.example.com", "uploads/2026/01/19/test.jpg", "https://cdn.example.com/uploads/2026/01/19/test.jpg")]
    [InlineData("http://cdn.example.com", "uploads/2026/01/19/test.jpg", "http://cdn.example.com/uploads/2026/01/19/test.jpg")]
    [InlineData("https://cdn.example.com", "/uploads/2026/01/19/test.jpg", "https://cdn.example.com/uploads/2026/01/19/test.jpg")]
    public void GenerateAccessUrl_ShouldFormatCorrectly(string domain, string objectKey, string expectedUrl)
    {
        // Act
        var result = TencentCosProvider.GenerateAccessUrl(domain, objectKey);

        // Assert
        Assert.Equal(expectedUrl, result);
    }

    [Fact]
    public void GenerateUniqueFileName_ShouldPreserveExtension()
    {
        // Arrange
        var originalFileName = "test.jpg";

        // Act
        var result = TencentCosProvider.GenerateUniqueFileName(originalFileName);

        // Assert
        Assert.EndsWith(".jpg", result);
    }

    [Theory]
    [InlineData("test.JPG", ".jpg")]
    [InlineData("test.JPEG", ".jpeg")]
    [InlineData("test.PNG", ".png")]
    [InlineData("test.GIF", ".gif")]
    [InlineData("test.WebP", ".webp")]
    public void GenerateUniqueFileName_ShouldNormalizeExtensionToLowercase(string fileName, string expectedExtension)
    {
        // Act
        var result = TencentCosProvider.GenerateUniqueFileName(fileName);

        // Assert
        Assert.EndsWith(expectedExtension, result);
    }

    [Fact]
    public void GenerateUniqueFileName_ShouldGenerateDifferentNamesForSameFile()
    {
        // Arrange
        var fileName = "same_file.jpg";

        // Act
        var result1 = TencentCosProvider.GenerateUniqueFileName(fileName);
        Thread.Sleep(1); // 确保时间戳不同
        var result2 = TencentCosProvider.GenerateUniqueFileName(fileName);

        // Assert
        Assert.NotEqual(result1, result2);
    }

    [Fact]
    public void GenerateUniqueFileName_ShouldContainTimestamp()
    {
        // Arrange
        var fileName = "test.jpg";
        var now = DateTime.Now;

        // Act
        var result = TencentCosProvider.GenerateUniqueFileName(fileName);

        // Assert
        // 文件名格式: {timestamp}_{guid}.jpg
        // timestamp格式: yyyyMMddHHmmssfff
        Assert.Contains(now.ToString("yyyyMMdd"), result);
    }

    [Fact]
    public void GenerateUniqueFileName_ShouldContainGuidPart()
    {
        // Arrange
        var fileName = "test.jpg";

        // Act
        var result = TencentCosProvider.GenerateUniqueFileName(fileName);

        // Assert
        // 文件名格式: {timestamp}_{guid}.jpg
        // 应该包含下划线分隔符
        Assert.Contains("_", result);
        // 下划线后应该有8个字符（GUID前8位）+ 扩展名
        var parts = result.Split('_');
        Assert.Equal(2, parts.Length);
    }

    [Fact]
    public void GenerateAccessUrl_ShouldHandleDomainWithoutProtocol()
    {
        // Arrange
        var domain = "cdn.example.com";
        var objectKey = "uploads/test.jpg";

        // Act
        var result = TencentCosProvider.GenerateAccessUrl(domain, objectKey);

        // Assert
        Assert.StartsWith("https://", result);
    }

    [Fact]
    public void GenerateAccessUrl_ShouldPreserveHttpProtocol()
    {
        // Arrange
        var domain = "http://cdn.example.com";
        var objectKey = "uploads/test.jpg";

        // Act
        var result = TencentCosProvider.GenerateAccessUrl(domain, objectKey);

        // Assert
        Assert.StartsWith("http://", result);
        Assert.DoesNotContain("https://", result);
    }

    [Fact]
    public void GenerateAccessUrl_ShouldRemoveTrailingSlashFromDomain()
    {
        // Arrange
        var domain = "https://cdn.example.com/";
        var objectKey = "uploads/test.jpg";

        // Act
        var result = TencentCosProvider.GenerateAccessUrl(domain, objectKey);

        // Assert
        Assert.DoesNotContain("//uploads", result);
    }

    [Fact]
    public void GenerateAccessUrl_ShouldHandleObjectKeyWithLeadingSlash()
    {
        // Arrange
        var domain = "https://cdn.example.com";
        var objectKey = "/uploads/test.jpg";

        // Act
        var result = TencentCosProvider.GenerateAccessUrl(domain, objectKey);

        // Assert
        Assert.Equal("https://cdn.example.com/uploads/test.jpg", result);
    }

    #endregion
}


/// <summary>
/// TencentCosProvider 属性测试
/// **Property 4: 存储策略一致性 - COS URL格式**
/// **Validates: Requirements 3.3, 3.4**
/// </summary>
public class TencentCosProviderPropertyTests
{
    /// <summary>
    /// **Feature: image-upload-feature, Property 4: 存储策略一致性 - COS URL格式**
    /// *For any* 有效的Domain和objectKey，生成的URL SHALL 以配置的Domain开头
    /// **Validates: Requirements 3.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool CosStorage_UrlFormat_ShouldStartWithDomain(PositiveInt seed)
    {
        // 生成测试数据
        var domains = new[]
        {
            "https://cdn.example.com",
            "https://bucket.cos.ap-guangzhou.myqcloud.com",
            "cdn.test.com",
            "http://cdn.local.com"
        };
        var domain = domains[seed.Get % domains.Length];
        
        var objectKey = $"uploads/2026/01/{(seed.Get % 28) + 1:D2}/test_{seed.Get}.jpg";
        
        // Act
        var url = TencentCosProvider.GenerateAccessUrl(domain, objectKey);
        
        // 验证URL以正确的协议和域名开头
        var normalizedDomain = domain.TrimEnd('/');
        if (!normalizedDomain.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
            !normalizedDomain.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        {
            normalizedDomain = $"https://{normalizedDomain}";
        }
        
        return url.StartsWith(normalizedDomain);
    }

    /// <summary>
    /// **Feature: image-upload-feature, Property 4: 存储策略一致性 - COS URL格式**
    /// *For any* 有效的Domain和objectKey，生成的URL SHALL 包含完整的对象路径
    /// **Validates: Requirements 3.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool CosStorage_UrlFormat_ShouldContainObjectKey(PositiveInt seed)
    {
        var domain = "https://cdn.example.com";
        var objectKey = $"uploads/2026/01/{(seed.Get % 28) + 1:D2}/file_{seed.Get}.jpg";
        
        // Act
        var url = TencentCosProvider.GenerateAccessUrl(domain, objectKey);
        
        // 验证URL包含对象路径
        var normalizedKey = objectKey.StartsWith('/') ? objectKey : $"/{objectKey}";
        return url.EndsWith(normalizedKey) || url.Contains(objectKey);
    }

    /// <summary>
    /// **Feature: image-upload-feature, Property 3: 文件名唯一性**
    /// *For any* 两次调用GenerateUniqueFileName，即使使用相同的原始文件名，生成的文件名 SHALL 不同
    /// **Validates: Requirements 1.5**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool CosStorage_FileNames_ShouldBeUnique(PositiveInt seed)
    {
        var extensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp" };
        var extension = extensions[seed.Get % extensions.Length];
        var fileName = $"test{extension}";
        
        // 生成两个文件名
        var name1 = TencentCosProvider.GenerateUniqueFileName(fileName);
        Thread.Sleep(1); // 确保时间戳不同
        var name2 = TencentCosProvider.GenerateUniqueFileName(fileName);
        
        // 验证两个文件名不同
        return name1 != name2;
    }

    /// <summary>
    /// **Feature: image-upload-feature, Property 4: 存储策略一致性 - COS URL格式**
    /// *For any* 生成的文件名，SHALL 保留原始文件的扩展名（小写）
    /// **Validates: Requirements 3.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool CosStorage_FileName_ShouldPreserveExtension(PositiveInt seed)
    {
        var extensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp", ".JPG", ".PNG", ".WEBP" };
        var extension = extensions[seed.Get % extensions.Length];
        var fileName = $"original_{seed.Get}{extension}";
        
        // Act
        var uniqueName = TencentCosProvider.GenerateUniqueFileName(fileName);
        
        // 验证扩展名被保留（小写）
        var expectedExtension = extension.ToLowerInvariant();
        return uniqueName.EndsWith(expectedExtension);
    }

    /// <summary>
    /// **Feature: image-upload-feature, Property 4: 存储策略一致性 - COS URL格式**
    /// *For any* 生成的URL，SHALL 不包含双斜杠（除了协议部分）
    /// **Validates: Requirements 3.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool CosStorage_UrlFormat_ShouldNotContainDoubleSlashes(PositiveInt seed)
    {
        var domains = new[]
        {
            "https://cdn.example.com",
            "https://cdn.example.com/",
            "cdn.test.com",
            "cdn.test.com/"
        };
        var domain = domains[seed.Get % domains.Length];
        
        var objectKeys = new[]
        {
            "uploads/2026/01/19/test.jpg",
            "/uploads/2026/01/19/test.jpg"
        };
        var objectKey = objectKeys[seed.Get % objectKeys.Length];
        
        // Act
        var url = TencentCosProvider.GenerateAccessUrl(domain, objectKey);
        
        // 移除协议部分后检查是否有双斜杠
        var urlWithoutProtocol = url.Replace("https://", "").Replace("http://", "");
        return !urlWithoutProtocol.Contains("//");
    }
}
