using HoneyBox.Core.Interfaces;
using HoneyBox.Core.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Auth;
using HoneyBox.Model.Models.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using Moq.Protected;
using System.Net;
using System.Text;
using Xunit;

namespace HoneyBox.Tests.Integration;

/// <summary>
/// 微信支付服务集成测试
/// 测试统一下单流程（模拟微信API）和签名生成验证
/// Requirements: 1.1-1.5, 7.1-7.4
/// </summary>
public class WechatPayServiceIntegrationTests
{
    private readonly WechatPaySettings _settings;
    private readonly AppSettings _appSettings;
    private readonly Mock<ILogger<WechatPayService>> _mockLogger;
    private readonly Mock<IWechatPayConfigService> _mockConfigService;
    private readonly Mock<IWechatService> _mockWechatService;
    private readonly Mock<IRedisService> _mockRedisService;

    public WechatPayServiceIntegrationTests()
    {
        _settings = new WechatPaySettings
        {
            DefaultMerchant = new WechatPayMerchantConfig
            {
                Name = "TestMerchant",
                MchId = "1234567890",
                AppId = "wx1234567890abcdef",
                Key = "test_secret_key_32_characters_ok",
                OrderPrefix = "TST",
                Weight = 1,
                NotifyUrl = "https://example.com/notify"
            },
            Merchants = new List<WechatPayMerchantConfig>
            {
                new WechatPayMerchantConfig
                {
                    Name = "Merchant1",
                    MchId = "1111111111",
                    AppId = "wx1111111111111111",
                    Key = "merchant1_secret_key_32_chars_ok",
                    OrderPrefix = "M01",
                    Weight = 1
                }
            },
            NotifyBaseUrl = "https://example.com"
        };

        _appSettings = new AppSettings { IsTestEnvironment = false };
        _mockLogger = new Mock<ILogger<WechatPayService>>();
        _mockConfigService = new Mock<IWechatPayConfigService>();
        _mockConfigService.Setup(x => x.GetMerchantByOrderNo(It.IsAny<string>()))
            .Returns(_settings.DefaultMerchant);
        _mockWechatService = new Mock<IWechatService>();
        _mockRedisService = new Mock<IRedisService>();
    }

    private HoneyBoxDbContext CreateInMemoryDbContext()
    {
        var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning))
            .Options;

        return new HoneyBoxDbContext(options);
    }

    private WechatPayService CreateWechatPayService(HoneyBoxDbContext dbContext, HttpClient? httpClient = null)
    {
        var options = Options.Create(_settings);
        return new WechatPayService(
            dbContext,
            httpClient ?? new HttpClient(),
            _mockLogger.Object,
            _mockConfigService.Object,
            _mockWechatService.Object,
            _mockRedisService.Object,
            options,
            _appSettings);
    }

    private async Task<User> CreateTestUserAsync(HoneyBoxDbContext dbContext)
    {
        var user = new User
        {
            Id = 1,
            OpenId = "test_openid_123456",
            Uid = "test_uid",
            Nickname = "测试用户",
            HeadImg = "avatar.jpg",
            Mobile = "13800138000",
            Money = 100,
            Integral = 1000,
            Money2 = 500,
            IsTest = 0,
            Status = 1,
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        await dbContext.Users.AddAsync(user);
        await dbContext.SaveChangesAsync();
        return user;
    }

    #region 签名生成和验证测试 (Requirements 7.1-7.4)

    /// <summary>
    /// 测试签名生成 - 基本功能
    /// Requirements: 7.1
    /// </summary>
    [Fact]
    public void MakeSign_BasicParameters_GeneratesValidSignature()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        var parameters = new Dictionary<string, string>
        {
            { "appid", "wx1234567890abcdef" },
            { "mch_id", "1234567890" },
            { "nonce_str", "test_nonce_string" },
            { "body", "Test Payment" },
            { "out_trade_no", "TST_20250102123456" },
            { "total_fee", "100" }
        };

        // Act
        var sign = service.MakeSign(parameters);

        // Assert
        Assert.NotNull(sign);
        Assert.Equal(32, sign.Length); // MD5 produces 32 hex characters
        Assert.Equal(sign, sign.ToUpper()); // Should be uppercase
        Assert.True(sign.All(c => "0123456789ABCDEF".Contains(c))); // Should be hex
    }

    /// <summary>
    /// 测试签名验证 - 正确签名
    /// Requirements: 7.2
    /// </summary>
    [Fact]
    public void VerifySign_ValidSignature_ReturnsTrue()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        var parameters = new Dictionary<string, string>
        {
            { "appid", "wx1234567890abcdef" },
            { "mch_id", "1234567890" },
            { "nonce_str", "test_nonce" },
            { "body", "Test Payment" }
        };

        var sign = service.MakeSign(parameters);

        // Act
        var result = service.VerifySign(parameters, sign);

        // Assert
        Assert.True(result);
    }

    /// <summary>
    /// 测试签名验证 - 错误签名
    /// Requirements: 7.4
    /// </summary>
    [Fact]
    public void VerifySign_InvalidSignature_ReturnsFalse()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        var parameters = new Dictionary<string, string>
        {
            { "appid", "wx1234567890abcdef" },
            { "mch_id", "1234567890" },
            { "nonce_str", "test_nonce" }
        };

        // Act
        var result = service.VerifySign(parameters, "INVALID_SIGNATURE_12345678901234");

        // Assert
        Assert.False(result);
    }

    /// <summary>
    /// 测试签名验证 - 空签名
    /// Requirements: 7.4
    /// </summary>
    [Fact]
    public void VerifySign_EmptySignature_ReturnsFalse()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        var parameters = new Dictionary<string, string>
        {
            { "appid", "wx1234567890abcdef" },
            { "mch_id", "1234567890" }
        };

        // Act & Assert
        Assert.False(service.VerifySign(parameters, ""));
        Assert.False(service.VerifySign(parameters, null!));
    }

    /// <summary>
    /// 测试多商户签名支持
    /// Requirements: 7.3
    /// </summary>
    [Fact]
    public void MakeSign_DifferentMerchantKeys_ProducesDifferentSignatures()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        var parameters = new Dictionary<string, string>
        {
            { "appid", "wx1234567890abcdef" },
            { "mch_id", "1234567890" },
            { "nonce_str", "test_nonce" }
        };

        var key1 = _settings.DefaultMerchant.Key;
        var key2 = _settings.Merchants[0].Key;

        // Act
        var sign1 = service.MakeSign(parameters, key1);
        var sign2 = service.MakeSign(parameters, key2);

        // Assert
        Assert.NotEqual(sign1, sign2);
        Assert.True(service.VerifySign(parameters, sign1, key1));
        Assert.False(service.VerifySign(parameters, sign1, key2));
    }

    #endregion

    #region 统一下单测试 (Requirements 1.1-1.5)

    /// <summary>
    /// 测试统一下单 - 用户不存在
    /// Requirements: 1.1
    /// </summary>
    [Fact]
    public async Task CreatePaymentAsync_UserNotFound_ReturnsError()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        var request = new WechatPayRequest
        {
            OrderNo = "TST_20250102123456",
            Amount = 10.00m,
            Body = "Test Payment",
            Attach = "order_yfs",
            UserId = 999 // Non-existent user
        };

        // Act
        var result = await service.CreatePaymentAsync(request);

        // Assert
        Assert.Equal(0, result.Status);
        Assert.Contains("用户不存在", result.Msg);
    }

    /// <summary>
    /// 测试统一下单 - 用户OpenId为空
    /// Requirements: 1.1
    /// </summary>
    [Fact]
    public async Task CreatePaymentAsync_EmptyOpenId_ReturnsError()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        
        // Create user without OpenId
        var user = new User
        {
            Id = 1,
            OpenId = "", // Empty OpenId
            Uid = "test_uid",
            Nickname = "测试用户",
            HeadImg = "avatar.jpg",
            Status = 1,
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        await dbContext.Users.AddAsync(user);
        await dbContext.SaveChangesAsync();

        var service = CreateWechatPayService(dbContext);

        var request = new WechatPayRequest
        {
            OrderNo = "TST_20250102123456",
            Amount = 10.00m,
            Body = "Test Payment",
            Attach = "order_yfs",
            UserId = 1
        };

        // Act
        var result = await service.CreatePaymentAsync(request);

        // Assert
        Assert.Equal(0, result.Status);
        Assert.Contains("OpenId", result.Msg);
    }

    /// <summary>
    /// 测试统一下单 - 成功场景（模拟微信API）
    /// Requirements: 1.1, 1.2, 1.4, 1.5
    /// </summary>
    [Fact]
    public async Task CreatePaymentAsync_ValidRequest_ReturnsPaymentParams()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        await CreateTestUserAsync(dbContext);

        // Mock HTTP response from WeChat API
        var mockHandler = new Mock<HttpMessageHandler>();
        var wechatResponse = @"<xml>
            <return_code><![CDATA[SUCCESS]]></return_code>
            <return_msg><![CDATA[OK]]></return_msg>
            <result_code><![CDATA[SUCCESS]]></result_code>
            <appid><![CDATA[wx1234567890abcdef]]></appid>
            <mch_id><![CDATA[1234567890]]></mch_id>
            <nonce_str><![CDATA[test_nonce]]></nonce_str>
            <sign><![CDATA[TEST_SIGN]]></sign>
            <prepay_id><![CDATA[wx201501021234567890]]></prepay_id>
            <trade_type><![CDATA[JSAPI]]></trade_type>
        </xml>";

        mockHandler.Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(wechatResponse, Encoding.UTF8, "application/xml")
            });

        var httpClient = new HttpClient(mockHandler.Object);
        var service = CreateWechatPayService(dbContext, httpClient);

        var request = new WechatPayRequest
        {
            OrderNo = "TST_20250102123456",
            Amount = 10.00m,
            Body = "Test Payment",
            Attach = "order_yfs",
            UserId = 1
        };

        // Act
        var result = await service.CreatePaymentAsync(request);

        // Assert
        Assert.Equal(1, result.Status);
        Assert.Equal("success", result.Msg);
        Assert.NotNull(result.Data);
        Assert.Equal("wx1234567890abcdef", result.Data.AppId);
        Assert.NotEmpty(result.Data.TimeStamp);
        Assert.NotEmpty(result.Data.NonceStr);
        Assert.Contains("prepay_id=", result.Data.Package);
        Assert.Equal("MD5", result.Data.SignType);
        Assert.NotEmpty(result.Data.PaySign);
        Assert.Equal(1, result.Data.IsWeixin);
    }

    /// <summary>
    /// 测试统一下单 - 微信API返回失败
    /// Requirements: 1.3
    /// </summary>
    [Fact]
    public async Task CreatePaymentAsync_WechatApiError_ReturnsError()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        await CreateTestUserAsync(dbContext);

        // Mock HTTP response with error
        var mockHandler = new Mock<HttpMessageHandler>();
        var wechatResponse = @"<xml>
            <return_code><![CDATA[FAIL]]></return_code>
            <return_msg><![CDATA[签名错误]]></return_msg>
        </xml>";

        mockHandler.Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(wechatResponse, Encoding.UTF8, "application/xml")
            });

        var httpClient = new HttpClient(mockHandler.Object);
        var service = CreateWechatPayService(dbContext, httpClient);

        var request = new WechatPayRequest
        {
            OrderNo = "TST_20250102123456",
            Amount = 10.00m,
            Body = "Test Payment",
            Attach = "order_yfs",
            UserId = 1
        };

        // Act
        var result = await service.CreatePaymentAsync(request);

        // Assert
        Assert.Equal(0, result.Status);
        Assert.Contains("签名错误", result.Msg);
    }

    /// <summary>
    /// 测试统一下单 - 业务失败（如订单已支付）
    /// Requirements: 1.3
    /// </summary>
    [Fact]
    public async Task CreatePaymentAsync_BusinessError_ReturnsError()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        await CreateTestUserAsync(dbContext);

        // Mock HTTP response with business error
        var mockHandler = new Mock<HttpMessageHandler>();
        var wechatResponse = @"<xml>
            <return_code><![CDATA[SUCCESS]]></return_code>
            <return_msg><![CDATA[OK]]></return_msg>
            <result_code><![CDATA[FAIL]]></result_code>
            <err_code><![CDATA[ORDERPAID]]></err_code>
            <err_code_des><![CDATA[该订单已支付]]></err_code_des>
        </xml>";

        mockHandler.Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(wechatResponse, Encoding.UTF8, "application/xml")
            });

        var httpClient = new HttpClient(mockHandler.Object);
        var service = CreateWechatPayService(dbContext, httpClient);

        var request = new WechatPayRequest
        {
            OrderNo = "TST_20250102123456",
            Amount = 10.00m,
            Body = "Test Payment",
            Attach = "order_yfs",
            UserId = 1
        };

        // Act
        var result = await service.CreatePaymentAsync(request);

        // Assert
        Assert.Equal(0, result.Status);
        Assert.Contains("已支付", result.Msg);
    }

    #endregion

    #region XML解析测试

    /// <summary>
    /// 测试XML解析 - 正常回调数据
    /// </summary>
    [Fact]
    public void ParseNotifyXml_ValidXml_ReturnsCorrectData()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        var xml = @"<xml>
            <return_code><![CDATA[SUCCESS]]></return_code>
            <return_msg><![CDATA[OK]]></return_msg>
            <result_code><![CDATA[SUCCESS]]></result_code>
            <appid><![CDATA[wx1234567890abcdef]]></appid>
            <mch_id><![CDATA[1234567890]]></mch_id>
            <nonce_str><![CDATA[test_nonce]]></nonce_str>
            <sign><![CDATA[TEST_SIGN]]></sign>
            <openid><![CDATA[test_openid_123456]]></openid>
            <trade_type><![CDATA[JSAPI]]></trade_type>
            <bank_type><![CDATA[CMC]]></bank_type>
            <total_fee>1000</total_fee>
            <fee_type><![CDATA[CNY]]></fee_type>
            <cash_fee>1000</cash_fee>
            <transaction_id><![CDATA[wx20250102123456]]></transaction_id>
            <out_trade_no><![CDATA[TST_20250102123456]]></out_trade_no>
            <attach><![CDATA[order_yfs]]></attach>
            <time_end><![CDATA[20250102123456]]></time_end>
        </xml>";

        // Act
        var result = service.ParseNotifyXml(xml);

        // Assert
        Assert.Equal("SUCCESS", result.ReturnCode);
        Assert.Equal("SUCCESS", result.ResultCode);
        Assert.Equal("wx1234567890abcdef", result.AppId);
        Assert.Equal("1234567890", result.MchId);
        Assert.Equal("test_openid_123456", result.OpenId);
        Assert.Equal(1000, result.TotalFee);
        Assert.Equal("TST_20250102123456", result.OutTradeNo);
        Assert.Equal("order_yfs", result.Attach);
    }

    /// <summary>
    /// 测试生成回调响应XML
    /// </summary>
    [Fact]
    public void GenerateNotifyResponseXml_Success_ReturnsCorrectXml()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        // Act
        var result = service.GenerateNotifyResponseXml("SUCCESS", "OK");

        // Assert
        Assert.Contains("<return_code><![CDATA[SUCCESS]]></return_code>", result);
        Assert.Contains("<return_msg><![CDATA[OK]]></return_msg>", result);
    }

    #endregion

    #region 商户配置测试 (Requirements 1.5)

    /// <summary>
    /// 测试根据订单号获取商户配置
    /// Requirements: 1.5
    /// </summary>
    [Fact]
    public void GetMerchantByOrderNo_ValidOrderNo_ReturnsCorrectMerchant()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        // Act
        var merchant = service.GetMerchantByOrderNo("TST_20250102123456");

        // Assert
        Assert.NotNull(merchant);
        Assert.Equal(_settings.DefaultMerchant.MchId, merchant.MchId);
    }

    /// <summary>
    /// 测试根据订单号获取商户密钥
    /// Requirements: 1.5
    /// </summary>
    [Fact]
    public void GetMerchantKeyByOrderNo_ValidOrderNo_ReturnsCorrectKey()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        var service = CreateWechatPayService(dbContext);

        // Act
        var key = service.GetMerchantKeyByOrderNo("TST_20250102123456");

        // Assert
        Assert.Equal(_settings.DefaultMerchant.Key, key);
    }

    #endregion
}
