using System.Text.Json;
using FsCheck;
using FsCheck.Xunit;
using HoneyBox.Model.Models.Payment;
using Xunit;

namespace HoneyBox.Tests.Services;

/// <summary>
/// 微信支付 V3 请求字段完整性属性测试
/// **Feature: wechat-pay-v3-upgrade**
/// </summary>
public class WechatPayV3RequestPropertyTests
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
        DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
    };

    #region Property 6: V3 请求字段完整性

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 6: V3 请求字段完整性**
    /// *For any* V3 JSAPI 下单请求，构建的请求体应该包含所有必要字段：
    /// appid、mchid、description、out_trade_no、notify_url、amount、payer。
    /// **Validates: Requirements 3.2**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3JsapiRequest_ShouldContainAllRequiredFields(
        NonEmptyString appId,
        NonEmptyString mchId,
        NonEmptyString description,
        NonEmptyString outTradeNo,
        NonEmptyString notifyUrl,
        PositiveInt totalAmount,
        NonEmptyString openId)
    {
        // 创建 V3 JSAPI 请求
        var request = new WechatPayV3JsapiRequest
        {
            AppId = appId.Get,
            MchId = mchId.Get,
            Description = description.Get,
            OutTradeNo = outTradeNo.Get,
            NotifyUrl = notifyUrl.Get,
            Amount = new WechatPayV3Amount
            {
                Total = totalAmount.Get,
                Currency = "CNY"
            },
            Payer = new WechatPayV3Payer
            {
                OpenId = openId.Get
            }
        };

        // 序列化为 JSON
        var json = JsonSerializer.Serialize(request, JsonOptions);

        // 验证所有必要字段都存在
        using var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        // 检查所有必要字段
        var hasAppId = root.TryGetProperty("appid", out var appIdProp) && !string.IsNullOrEmpty(appIdProp.GetString());
        var hasMchId = root.TryGetProperty("mchid", out var mchIdProp) && !string.IsNullOrEmpty(mchIdProp.GetString());
        var hasDescription = root.TryGetProperty("description", out var descProp) && !string.IsNullOrEmpty(descProp.GetString());
        var hasOutTradeNo = root.TryGetProperty("out_trade_no", out var tradeProp) && !string.IsNullOrEmpty(tradeProp.GetString());
        var hasNotifyUrl = root.TryGetProperty("notify_url", out var notifyProp) && !string.IsNullOrEmpty(notifyProp.GetString());
        var hasAmount = root.TryGetProperty("amount", out var amountProp) && amountProp.ValueKind == JsonValueKind.Object;
        var hasPayer = root.TryGetProperty("payer", out var payerProp) && payerProp.ValueKind == JsonValueKind.Object;

        // 检查 amount 子字段
        var hasTotal = hasAmount && amountProp.TryGetProperty("total", out var totalProp) && totalProp.ValueKind == JsonValueKind.Number;
        var hasCurrency = hasAmount && amountProp.TryGetProperty("currency", out var currencyProp) && !string.IsNullOrEmpty(currencyProp.GetString());

        // 检查 payer 子字段
        var hasOpenId = hasPayer && payerProp.TryGetProperty("openid", out var openIdProp) && !string.IsNullOrEmpty(openIdProp.GetString());

        return hasAppId && hasMchId && hasDescription && hasOutTradeNo && hasNotifyUrl &&
               hasAmount && hasTotal && hasCurrency && hasPayer && hasOpenId;
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 6: V3 请求字段完整性**
    /// *For any* V3 JSAPI 请求，金额字段应该是正整数（单位：分）。
    /// **Validates: Requirements 3.2**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3JsapiRequest_AmountShouldBePositiveInteger(PositiveInt totalAmount)
    {
        var request = new WechatPayV3JsapiRequest
        {
            AppId = "wx1234567890",
            MchId = "1234567890",
            Description = "测试商品",
            OutTradeNo = "ORDER123456",
            NotifyUrl = "https://example.com/notify",
            Amount = new WechatPayV3Amount
            {
                Total = totalAmount.Get,
                Currency = "CNY"
            },
            Payer = new WechatPayV3Payer
            {
                OpenId = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
            }
        };

        var json = JsonSerializer.Serialize(request, JsonOptions);
        using var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        var amount = root.GetProperty("amount");
        var total = amount.GetProperty("total").GetInt32();

        return total > 0 && total == totalAmount.Get;
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 6: V3 请求字段完整性**
    /// *For any* V3 JSAPI 请求，货币类型默认应该是 CNY。
    /// **Validates: Requirements 3.2**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3JsapiRequest_CurrencyShouldDefaultToCNY(PositiveInt totalAmount)
    {
        var request = new WechatPayV3JsapiRequest
        {
            AppId = "wx1234567890",
            MchId = "1234567890",
            Description = "测试商品",
            OutTradeNo = "ORDER123456",
            NotifyUrl = "https://example.com/notify",
            Amount = new WechatPayV3Amount
            {
                Total = totalAmount.Get
                // Currency 使用默认值
            },
            Payer = new WechatPayV3Payer
            {
                OpenId = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
            }
        };

        var json = JsonSerializer.Serialize(request, JsonOptions);
        using var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        var amount = root.GetProperty("amount");
        var currency = amount.GetProperty("currency").GetString();

        return currency == "CNY";
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 6: V3 请求字段完整性**
    /// *For any* V3 JSAPI 请求，可选字段 attach 为 null 时不应该出现在 JSON 中。
    /// **Validates: Requirements 3.2**
    /// </summary>
    [Fact]
    public void V3JsapiRequest_NullAttach_ShouldNotAppearInJson()
    {
        var request = new WechatPayV3JsapiRequest
        {
            AppId = "wx1234567890",
            MchId = "1234567890",
            Description = "测试商品",
            OutTradeNo = "ORDER123456",
            NotifyUrl = "https://example.com/notify",
            Amount = new WechatPayV3Amount { Total = 100, Currency = "CNY" },
            Payer = new WechatPayV3Payer { OpenId = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o" },
            Attach = null // 可选字段为 null
        };

        var json = JsonSerializer.Serialize(request, JsonOptions);
        using var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        // attach 为 null 时不应该出现在 JSON 中
        Assert.False(root.TryGetProperty("attach", out _));
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 6: V3 请求字段完整性**
    /// *For any* V3 JSAPI 请求，可选字段 attach 有值时应该出现在 JSON 中。
    /// **Validates: Requirements 3.2**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3JsapiRequest_NonNullAttach_ShouldAppearInJson(NonEmptyString attach)
    {
        var request = new WechatPayV3JsapiRequest
        {
            AppId = "wx1234567890",
            MchId = "1234567890",
            Description = "测试商品",
            OutTradeNo = "ORDER123456",
            NotifyUrl = "https://example.com/notify",
            Amount = new WechatPayV3Amount { Total = 100, Currency = "CNY" },
            Payer = new WechatPayV3Payer { OpenId = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o" },
            Attach = attach.Get
        };

        var json = JsonSerializer.Serialize(request, JsonOptions);
        using var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        // attach 有值时应该出现在 JSON 中
        return root.TryGetProperty("attach", out var attachProp) &&
               attachProp.GetString() == attach.Get;
    }

    #endregion

    #region Property 7: V3 支付参数完整性

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 7: V3 支付参数完整性**
    /// *For any* 成功的 V3 下单响应，返回给前端的支付参数应该包含：
    /// timeStamp、nonceStr、package、signType(RSA)、paySign。
    /// **Validates: Requirements 3.4**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3PayData_ShouldContainAllRequiredFields(
        NonEmptyString appId,
        NonEmptyString timeStamp,
        NonEmptyString nonceStr,
        NonEmptyString prepayId,
        NonEmptyString paySign)
    {
        // 模拟 V3 支付数据
        var payData = new WechatPayData
        {
            AppId = appId.Get,
            TimeStamp = timeStamp.Get,
            NonceStr = nonceStr.Get,
            Package = $"prepay_id={prepayId.Get}",
            SignType = "RSA",
            PaySign = paySign.Get,
            IsWeixin = 1
        };

        // 验证所有必要字段
        return !string.IsNullOrEmpty(payData.AppId) &&
               !string.IsNullOrEmpty(payData.TimeStamp) &&
               !string.IsNullOrEmpty(payData.NonceStr) &&
               !string.IsNullOrEmpty(payData.Package) &&
               payData.Package.StartsWith("prepay_id=") &&
               payData.SignType == "RSA" &&
               !string.IsNullOrEmpty(payData.PaySign);
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 7: V3 支付参数完整性**
    /// V3 支付参数的 signType 应该是 RSA（而不是 V2 的 MD5）。
    /// **Validates: Requirements 3.4**
    /// </summary>
    [Fact]
    public void V3PayData_SignType_ShouldBeRSA()
    {
        var payData = new WechatPayData
        {
            AppId = "wx1234567890",
            TimeStamp = "1609459200",
            NonceStr = "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
            Package = "prepay_id=wx201410272009395522657a690389285100",
            SignType = "RSA", // V3 使用 RSA
            PaySign = "base64signature",
            IsWeixin = 1
        };

        Assert.Equal("RSA", payData.SignType);
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 7: V3 支付参数完整性**
    /// V3 支付参数的 package 格式应该是 prepay_id=xxx。
    /// **Validates: Requirements 3.4**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3PayData_Package_ShouldHaveCorrectFormat(NonEmptyString prepayId)
    {
        var package = $"prepay_id={prepayId.Get}";

        return package.StartsWith("prepay_id=") &&
               package.Length > "prepay_id=".Length;
    }

    #endregion

    #region 请求序列化测试

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 6: V3 请求字段完整性**
    /// V3 请求序列化后的 JSON 字段名应该使用 snake_case 格式。
    /// **Validates: Requirements 3.2**
    /// </summary>
    [Fact]
    public void V3JsapiRequest_Serialization_ShouldUseSnakeCase()
    {
        var request = new WechatPayV3JsapiRequest
        {
            AppId = "wx1234567890",
            MchId = "1234567890",
            Description = "测试商品",
            OutTradeNo = "ORDER123456",
            NotifyUrl = "https://example.com/notify",
            Amount = new WechatPayV3Amount { Total = 100, Currency = "CNY" },
            Payer = new WechatPayV3Payer { OpenId = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o" }
        };

        var json = JsonSerializer.Serialize(request, JsonOptions);

        // 验证使用 snake_case
        Assert.Contains("\"appid\"", json);
        Assert.Contains("\"mchid\"", json);
        Assert.Contains("\"description\"", json);
        Assert.Contains("\"out_trade_no\"", json);
        Assert.Contains("\"notify_url\"", json);
        Assert.Contains("\"amount\"", json);
        Assert.Contains("\"payer\"", json);
        Assert.Contains("\"total\"", json);
        Assert.Contains("\"currency\"", json);
        Assert.Contains("\"openid\"", json);
    }

    /// <summary>
    /// **Feature: wechat-pay-v3-upgrade, Property 6: V3 请求字段完整性**
    /// V3 请求序列化后应该是有效的 JSON。
    /// **Validates: Requirements 3.2**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool V3JsapiRequest_Serialization_ShouldProduceValidJson(
        NonEmptyString appId,
        NonEmptyString mchId,
        NonEmptyString description,
        NonEmptyString outTradeNo,
        NonEmptyString notifyUrl,
        PositiveInt totalAmount,
        NonEmptyString openId)
    {
        var request = new WechatPayV3JsapiRequest
        {
            AppId = appId.Get,
            MchId = mchId.Get,
            Description = description.Get,
            OutTradeNo = outTradeNo.Get,
            NotifyUrl = notifyUrl.Get,
            Amount = new WechatPayV3Amount { Total = totalAmount.Get, Currency = "CNY" },
            Payer = new WechatPayV3Payer { OpenId = openId.Get }
        };

        try
        {
            var json = JsonSerializer.Serialize(request, JsonOptions);
            using var doc = JsonDocument.Parse(json);
            return doc.RootElement.ValueKind == JsonValueKind.Object;
        }
        catch
        {
            return false;
        }
    }

    #endregion
}
