using FsCheck;
using FsCheck.Xunit;
using HoneyBox.Core.Interfaces;
using HoneyBox.Core.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Models.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace HoneyBox.Tests.Services;

/// <summary>
/// 微信支付 V3 回调格式识别属性测试
/// **Feature: wechat-pay-v3-upgrade**
/// </summary>
public class WechatPayV3NotifyFormatPropertyTests
{
    private IWechatPayV3Service CreateService()
    {
        var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;
        var dbContext = new HoneyBoxDbContext(options);
        var httpClient = new HttpClient();
        var logger = Mock.Of<ILogger<WechatPayV3Service>>();
        var configService = Mock.Of<IWechatPayConfigService>();

        return new WechatPayV3Service(dbContext, httpClient, logger, configService);
    }

    #region Property 8: 回调格式识别正确性

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// *For any* JSON 格式且包含 resource 字段的回调数据，应该被识别为 V3 格式。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3Format_JsonWithResource_ShouldBeDetectedAsV3(
        NonEmptyString notifyId,
        NonEmptyString eventType,
        NonEmptyString ciphertext,
        PositiveInt seed)
    {
        var service = CreateService();

        // 清理输入（移除控制字符和特殊字符）
        var cleanNotifyId = CleanJsonString(RemoveControlChars(notifyId.Get));
        var cleanEventType = CleanJsonString(RemoveControlChars(eventType.Get));
        var cleanCiphertext = CleanJsonString(RemoveControlChars(ciphertext.Get));

        // 如果清理后为空，跳过测试
        if (string.IsNullOrEmpty(cleanNotifyId) || 
            string.IsNullOrEmpty(cleanEventType) || 
            string.IsNullOrEmpty(cleanCiphertext))
        {
            return true;
        }

        // 构建 V3 格式的回调数据（JSON 格式且包含 resource 字段）
        var v3NotifyBody = $@"{{
            ""id"": ""{cleanNotifyId}"",
            ""create_time"": ""2024-01-01T12:00:00+08:00"",
            ""event_type"": ""{cleanEventType}"",
            ""resource_type"": ""encrypt-resource"",
            ""resource"": {{
                ""algorithm"": ""AEAD_AES_256_GCM"",
                ""ciphertext"": ""{cleanCiphertext}"",
                ""nonce"": ""abcdefghijkl"",
                ""associated_data"": ""transaction""
            }}
        }}";

        var isV3 = service.IsV3NotifyFormat(v3NotifyBody);
        var isV2 = service.IsV2NotifyFormat(v3NotifyBody);
        var version = service.DetectNotifyVersion(v3NotifyBody);

        // V3 格式应该被正确识别
        return isV3 && !isV2 && version == NotifyVersion.V3;
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// *For any* XML 格式的回调数据，应该被识别为 V2 格式。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V2Format_Xml_ShouldBeDetectedAsV2(
        NonEmptyString orderNo,
        NonEmptyString transactionId,
        PositiveInt totalFee,
        PositiveInt seed)
    {
        var service = CreateService();

        // 清理输入（移除控制字符）
        var cleanOrderNo = CleanXmlString(RemoveControlChars(orderNo.Get));
        var cleanTransactionId = CleanXmlString(RemoveControlChars(transactionId.Get));

        // 如果清理后为空，跳过测试
        if (string.IsNullOrEmpty(cleanOrderNo) || string.IsNullOrEmpty(cleanTransactionId))
        {
            return true;
        }

        // 构建 V2 格式的回调数据（XML 格式）
        var v2NotifyBody = $@"<xml>
            <return_code><![CDATA[SUCCESS]]></return_code>
            <result_code><![CDATA[SUCCESS]]></result_code>
            <out_trade_no><![CDATA[{cleanOrderNo}]]></out_trade_no>
            <transaction_id><![CDATA[{cleanTransactionId}]]></transaction_id>
            <total_fee>{totalFee.Get}</total_fee>
        </xml>";

        var isV3 = service.IsV3NotifyFormat(v2NotifyBody);
        var isV2 = service.IsV2NotifyFormat(v2NotifyBody);
        var version = service.DetectNotifyVersion(v2NotifyBody);

        // V2 格式应该被正确识别
        return !isV3 && isV2 && version == NotifyVersion.V2;
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// *For any* JSON 格式但不包含 resource 字段的数据，不应该被识别为 V3 格式。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool JsonWithoutResource_ShouldNotBeV3(
        NonEmptyString key,
        NonEmptyString value,
        PositiveInt seed)
    {
        var service = CreateService();

        var cleanKey = CleanJsonString(RemoveControlChars(key.Get));
        var cleanValue = CleanJsonString(RemoveControlChars(value.Get));

        // 如果清理后为空，跳过测试
        if (string.IsNullOrEmpty(cleanKey) || string.IsNullOrEmpty(cleanValue))
        {
            return true;
        }

        // 确保 key 不是 "resource"
        if (cleanKey.Equals("resource", StringComparison.OrdinalIgnoreCase))
        {
            cleanKey = "other_key";
        }

        // 构建不包含 resource 字段的 JSON
        var jsonBody = $@"{{
            ""{cleanKey}"": ""{cleanValue}"",
            ""other_field"": ""some_value""
        }}";

        var isV3 = service.IsV3NotifyFormat(jsonBody);

        // 不包含 resource 字段的 JSON 不应该被识别为 V3
        return !isV3;
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// *For any* 空字符串或空白字符串，应该被识别为 Unknown 格式。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool EmptyOrWhitespace_ShouldBeUnknown(PositiveInt whitespaceCount)
    {
        var service = CreateService();

        // 生成空白字符串
        var whitespace = new string(' ', whitespaceCount.Get % 100);

        var isV3Empty = service.IsV3NotifyFormat("");
        var isV2Empty = service.IsV2NotifyFormat("");
        var versionEmpty = service.DetectNotifyVersion("");

        var isV3Whitespace = service.IsV3NotifyFormat(whitespace);
        var isV2Whitespace = service.IsV2NotifyFormat(whitespace);
        var versionWhitespace = service.DetectNotifyVersion(whitespace);

        // 空字符串和空白字符串都应该被识别为 Unknown
        return !isV3Empty && !isV2Empty && versionEmpty == NotifyVersion.Unknown &&
               !isV3Whitespace && !isV2Whitespace && versionWhitespace == NotifyVersion.Unknown;
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// *For any* 非 JSON 非 XML 的数据，应该被识别为 Unknown 格式。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool InvalidFormat_ShouldBeUnknown(NonEmptyString randomData, PositiveInt seed)
    {
        var service = CreateService();

        // 确保数据不是以 { 或 < 开头
        var data = randomData.Get.TrimStart();
        if (data.StartsWith('{') || data.StartsWith('<'))
        {
            data = "INVALID_" + data;
        }

        var isV3 = service.IsV3NotifyFormat(data);
        var isV2 = service.IsV2NotifyFormat(data);
        var version = service.DetectNotifyVersion(data);

        // 非 JSON 非 XML 的数据应该被识别为 Unknown
        return !isV3 && !isV2 && version == NotifyVersion.Unknown;
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// V3 和 V2 格式应该是互斥的。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3AndV2_ShouldBeMutuallyExclusive(NonEmptyString data, PositiveInt seed)
    {
        var service = CreateService();

        var isV3 = service.IsV3NotifyFormat(data.Get);
        var isV2 = service.IsV2NotifyFormat(data.Get);

        // V3 和 V2 不能同时为 true
        return !(isV3 && isV2);
    }

    #endregion

    #region 边界情况测试

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// 真实的 V3 支付成功回调应该被正确识别。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Fact]
    public void RealV3PaymentSuccessNotify_ShouldBeDetectedAsV3()
    {
        var service = CreateService();

        var v3NotifyBody = @"{
            ""id"": ""EV-2024010112345678901234567890"",
            ""create_time"": ""2024-01-01T12:00:00+08:00"",
            ""event_type"": ""TRANSACTION.SUCCESS"",
            ""resource_type"": ""encrypt-resource"",
            ""resource"": {
                ""algorithm"": ""AEAD_AES_256_GCM"",
                ""ciphertext"": ""base64encodedciphertext"",
                ""nonce"": ""abcdefghijkl"",
                ""associated_data"": ""transaction"",
                ""original_type"": ""transaction""
            },
            ""summary"": ""支付成功""
        }";

        Assert.True(service.IsV3NotifyFormat(v3NotifyBody));
        Assert.False(service.IsV2NotifyFormat(v3NotifyBody));
        Assert.Equal(NotifyVersion.V3, service.DetectNotifyVersion(v3NotifyBody));
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// 真实的 V2 支付成功回调应该被正确识别。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Fact]
    public void RealV2PaymentSuccessNotify_ShouldBeDetectedAsV2()
    {
        var service = CreateService();

        var v2NotifyBody = @"<xml>
            <return_code><![CDATA[SUCCESS]]></return_code>
            <return_msg><![CDATA[OK]]></return_msg>
            <appid><![CDATA[wx1234567890abcdef]]></appid>
            <mch_id><![CDATA[1234567890]]></mch_id>
            <nonce_str><![CDATA[5K8264ILTKCH16CQ2502SI8ZNMTM67VS]]></nonce_str>
            <sign><![CDATA[C380BEC2BFD727A4B6845133519F3AD6]]></sign>
            <result_code><![CDATA[SUCCESS]]></result_code>
            <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
            <trade_type><![CDATA[JSAPI]]></trade_type>
            <bank_type><![CDATA[CMC]]></bank_type>
            <total_fee>100</total_fee>
            <fee_type><![CDATA[CNY]]></fee_type>
            <transaction_id><![CDATA[1217752501201407033233368018]]></transaction_id>
            <out_trade_no><![CDATA[MYH20240101120000001]]></out_trade_no>
            <attach><![CDATA[order_yfs]]></attach>
            <time_end><![CDATA[20240101120000]]></time_end>
        </xml>";

        Assert.False(service.IsV3NotifyFormat(v2NotifyBody));
        Assert.True(service.IsV2NotifyFormat(v2NotifyBody));
        Assert.Equal(NotifyVersion.V2, service.DetectNotifyVersion(v2NotifyBody));
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// 带有前导空白的 V3 回调应该被正确识别。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Fact]
    public void V3NotifyWithLeadingWhitespace_ShouldBeDetectedAsV3()
    {
        var service = CreateService();

        var v3NotifyBody = @"   
        {
            ""id"": ""test"",
            ""resource"": {
                ""ciphertext"": ""test""
            }
        }";

        Assert.True(service.IsV3NotifyFormat(v3NotifyBody));
        Assert.Equal(NotifyVersion.V3, service.DetectNotifyVersion(v3NotifyBody));
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// 带有前导空白的 V2 回调应该被正确识别。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Fact]
    public void V2NotifyWithLeadingWhitespace_ShouldBeDetectedAsV2()
    {
        var service = CreateService();

        var v2NotifyBody = @"   
        <xml>
            <return_code>SUCCESS</return_code>
        </xml>";

        Assert.True(service.IsV2NotifyFormat(v2NotifyBody));
        Assert.Equal(NotifyVersion.V2, service.DetectNotifyVersion(v2NotifyBody));
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 8: 回调格式识别正确性**
    /// 无效的 JSON 不应该被识别为 V3。
    /// **Validates: Requirements 4.1, 4.5**
    /// </summary>
    [Fact]
    public void InvalidJson_ShouldNotBeV3()
    {
        var service = CreateService();

        var invalidJson = @"{ ""resource"": ""missing closing brace""";

        Assert.False(service.IsV3NotifyFormat(invalidJson));
    }

    #endregion

    #region 辅助方法

    /// <summary>
    /// 移除控制字符
    /// </summary>
    private static string RemoveControlChars(string input)
    {
        if (string.IsNullOrEmpty(input))
        {
            return input;
        }
        return new string(input.Where(c => !char.IsControl(c)).ToArray());
    }

    /// <summary>
    /// 清理字符串以用于 JSON
    /// </summary>
    private static string CleanJsonString(string input)
    {
        if (string.IsNullOrEmpty(input))
        {
            return input;
        }
        return input
            .Replace("\\", "\\\\")
            .Replace("\"", "\\\"")
            .Replace("\n", "\\n")
            .Replace("\r", "\\r")
            .Replace("\t", "\\t");
    }

    /// <summary>
    /// 清理字符串以用于 XML
    /// </summary>
    private static string CleanXmlString(string input)
    {
        if (string.IsNullOrEmpty(input))
        {
            return input;
        }
        return input
            .Replace("&", "&amp;")
            .Replace("<", "&lt;")
            .Replace(">", "&gt;")
            .Replace("\"", "&quot;")
            .Replace("'", "&apos;")
            .Replace("\n", " ")
            .Replace("\r", " ");
    }

    #endregion
}
