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

namespace HoneyBox.Tests.Services;

/// <summary>
/// Mock HttpMessageHandler for testing
/// </summary>
public class MockHttpMessageHandler : HttpMessageHandler
{
    private readonly Func<HttpRequestMessage, Task<HttpResponseMessage>> _handler;

    public MockHttpMessageHandler(Func<HttpRequestMessage, Task<HttpResponseMessage>> handler)
    {
        _handler = handler;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return await _handler(request);
    }
}

public class WechatServiceTests
{
    private readonly Mock<ILogger<WechatService>> _mockLogger;
    private readonly Mock<IOptions<WechatPaySettings>> _mockWechatPaySettings;
    private readonly Mock<IRedisService> _mockRedisService;

    public WechatServiceTests()
    {
        _mockLogger = new Mock<ILogger<WechatService>>();
        
        var wechatPaySettings = 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>()
        };
        _mockWechatPaySettings = new Mock<IOptions<WechatPaySettings>>();
        _mockWechatPaySettings.Setup(x => x.Value).Returns(wechatPaySettings);
        
        _mockRedisService = new Mock<IRedisService>();
    }

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

        return new HoneyBoxDbContext(options);
    }

    [Fact]
    public async Task GetOpenIdAsync_WithValidCode_ReturnsSuccessResult()
    {
        // Arrange
        var responseContent = @"{
            ""session_key"": ""test_session_key"",
            ""openid"": ""test_openid_123"",
            ""unionid"": ""test_unionid_456""
        }";

        var handler = new MockHttpMessageHandler(async request =>
        {
            return new HttpResponseMessage
            {
                StatusCode = System.Net.HttpStatusCode.OK,
                Content = new StringContent(responseContent)
            };
        });

        var httpClient = new HttpClient(handler);
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetOpenIdAsync("test_code");

        // Assert
        Assert.NotNull(result);
        Assert.True(result.Success);
        Assert.Equal("test_openid_123", result.OpenId);
        Assert.Equal("test_unionid_456", result.UnionId);
        Assert.Null(result.ErrorMessage);
    }

    [Fact]
    public async Task GetOpenIdAsync_WithEmptyCode_ReturnsFailureResult()
    {
        // Arrange
        var httpClient = new HttpClient();
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetOpenIdAsync(string.Empty);

        // Assert
        Assert.NotNull(result);
        Assert.False(result.Success);
        Assert.Null(result.OpenId);
        Assert.NotNull(result.ErrorMessage);
    }

    [Fact]
    public async Task GetOpenIdAsync_WithNullCode_ReturnsFailureResult()
    {
        // Arrange
        var httpClient = new HttpClient();
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetOpenIdAsync(null!);

        // Assert
        Assert.NotNull(result);
        Assert.False(result.Success);
        Assert.Null(result.OpenId);
        Assert.NotNull(result.ErrorMessage);
    }

    [Fact]
    public async Task GetOpenIdAsync_WithWechatError_ReturnsFailureResult()
    {
        // Arrange
        var responseContent = @"{
            ""errcode"": 40001,
            ""errmsg"": ""invalid credential""
        }";

        var handler = new MockHttpMessageHandler(async request =>
        {
            return new HttpResponseMessage
            {
                StatusCode = System.Net.HttpStatusCode.OK,
                Content = new StringContent(responseContent)
            };
        });

        var httpClient = new HttpClient(handler);
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetOpenIdAsync("invalid_code");

        // Assert
        Assert.NotNull(result);
        Assert.False(result.Success);
        Assert.Null(result.OpenId);
        Assert.NotNull(result.ErrorMessage);
        Assert.Contains("invalid credential", result.ErrorMessage);
    }

    [Fact]
    public async Task GetOpenIdAsync_WithHttpError_ReturnsFailureResult()
    {
        // Arrange
        var handler = new MockHttpMessageHandler(async request =>
        {
            return new HttpResponseMessage
            {
                StatusCode = System.Net.HttpStatusCode.InternalServerError,
                Content = new StringContent("Server Error")
            };
        });

        var httpClient = new HttpClient(handler);
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetOpenIdAsync("test_code");

        // Assert
        Assert.NotNull(result);
        Assert.False(result.Success);
        Assert.Null(result.OpenId);
        Assert.NotNull(result.ErrorMessage);
    }

    [Fact]
    public async Task GetMobileAsync_WithValidCode_ReturnsSuccessResult()
    {
        // Arrange
        var responseContent = @"{
            ""errcode"": 0,
            ""errmsg"": ""ok"",
            ""phone_info"": {
                ""phoneNumber"": ""13800138000"",
                ""purePhoneNumber"": ""13800138000"",
                ""countryCode"": ""86""
            }
        }";

        var handler = new MockHttpMessageHandler(async request =>
        {
            return new HttpResponseMessage
            {
                StatusCode = System.Net.HttpStatusCode.OK,
                Content = new StringContent(responseContent)
            };
        });

        var httpClient = new HttpClient(handler);
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetMobileAsync("test_access_token");

        // Assert
        Assert.NotNull(result);
        Assert.True(result.Success);
        Assert.Equal("13800138000", result.Mobile);
        Assert.Null(result.ErrorMessage);
    }

    [Fact]
    public async Task GetMobileAsync_WithEmptyCode_ReturnsFailureResult()
    {
        // Arrange
        var httpClient = new HttpClient();
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetMobileAsync(string.Empty);

        // Assert
        Assert.NotNull(result);
        Assert.False(result.Success);
        Assert.Null(result.Mobile);
        Assert.NotNull(result.ErrorMessage);
    }

    [Fact]
    public async Task GetMobileAsync_WithWechatError_ReturnsFailureResult()
    {
        // Arrange
        var responseContent = @"{
            ""errcode"": 40001,
            ""errmsg"": ""invalid access_token""
        }";

        var handler = new MockHttpMessageHandler(async request =>
        {
            return new HttpResponseMessage
            {
                StatusCode = System.Net.HttpStatusCode.OK,
                Content = new StringContent(responseContent)
            };
        });

        var httpClient = new HttpClient(handler);
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetMobileAsync("invalid_token");

        // Assert
        Assert.NotNull(result);
        Assert.False(result.Success);
        Assert.Null(result.Mobile);
        Assert.NotNull(result.ErrorMessage);
    }

    [Fact]
    public async Task GetMobileAsync_WithHttpError_ReturnsFailureResult()
    {
        // Arrange
        var handler = new MockHttpMessageHandler(async request =>
        {
            return new HttpResponseMessage
            {
                StatusCode = System.Net.HttpStatusCode.ServiceUnavailable,
                Content = new StringContent("Service Unavailable")
            };
        });

        var httpClient = new HttpClient(handler);
        var dbContext = CreateInMemoryDbContext();
        var wechatService = new WechatService(httpClient, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext);

        // Act
        var result = await wechatService.GetMobileAsync("test_token");

        // Assert
        Assert.NotNull(result);
        Assert.False(result.Success);
        Assert.Null(result.Mobile);
        Assert.NotNull(result.ErrorMessage);
    }

    [Fact]
    public void WechatService_WithNullHttpClient_ThrowsArgumentNullException()
    {
        // Arrange
        var dbContext = CreateInMemoryDbContext();
        
        // Act & Assert
        Assert.Throws<ArgumentNullException>(() => 
            new WechatService(null!, _mockLogger.Object, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext));
    }

    [Fact]
    public void WechatService_WithNullLogger_ThrowsArgumentNullException()
    {
        // Arrange
        var httpClient = new HttpClient();
        var dbContext = CreateInMemoryDbContext();

        // Act & Assert
        Assert.Throws<ArgumentNullException>(() => 
            new WechatService(httpClient, null!, _mockWechatPaySettings.Object, _mockRedisService.Object, dbContext));
    }

    [Fact]
    public void WechatService_WithNullWechatPaySettings_ThrowsArgumentNullException()
    {
        // Arrange
        var httpClient = new HttpClient();
        var dbContext = CreateInMemoryDbContext();

        // Act & Assert
        Assert.Throws<ArgumentNullException>(() => 
            new WechatService(httpClient, _mockLogger.Object, null!, _mockRedisService.Object, dbContext));
    }
}
