using HoneyBox.Admin.Business.Models.Finance;
using HoneyBox.Admin.Business.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace HoneyBox.Tests.Services;

/// <summary>
/// FinanceService 单元测试
/// </summary>
public class FinanceServiceTests : IDisposable
{
    private readonly HoneyBoxDbContext _dbContext;
    private readonly FinanceService _service;
    private readonly Mock<ILogger<FinanceService>> _mockLogger;

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

        _dbContext = new HoneyBoxDbContext(options);
        _mockLogger = new Mock<ILogger<FinanceService>>();
        _service = new FinanceService(_dbContext, _mockLogger.Object);
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }

    #region 消费排行榜测试

    [Fact]
    public async Task GetConsumptionRanking_ShouldReturnUsersOrderedByConsumption()
    {
        // Arrange
        SeedUsers(3);
        SeedOrders();

        var request = new FinanceQueryRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _service.GetConsumptionRankingAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.True(result.List.Count > 0);
        // 验证按消费金额降序排序
        for (int i = 0; i < result.List.Count - 1; i++)
        {
            Assert.True(result.List[i].TotalConsumption >= result.List[i + 1].TotalConsumption);
        }
    }

    [Fact]
    public async Task GetConsumptionRanking_ShouldIncludeUserInfo()
    {
        // Arrange
        SeedUsers(1);
        SeedOrders();

        var request = new FinanceQueryRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _service.GetConsumptionRankingAsync(request);

        // Assert
        Assert.NotNull(result);
        if (result.List.Count > 0)
        {
            var first = result.List[0];
            Assert.NotNull(first.Uid);
            Assert.NotNull(first.Nickname);
        }
    }

    #endregion

    #region 余额明细测试

    [Fact]
    public async Task GetBalanceDetails_ShouldReturnPaginatedResults()
    {
        // Arrange
        SeedUsers(1);
        SeedProfitMoney(15);

        var request = new FinanceQueryRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _service.GetBalanceDetailsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(15, result.Total);
        Assert.Equal(10, result.List.Count);
    }

    [Fact]
    public async Task GetBalanceDetails_ShouldFilterByUserId()
    {
        // Arrange
        SeedUsers(3);
        SeedProfitMoneyForMultipleUsers();

        var request = new FinanceQueryRequest { UserId = 1, Page = 1, PageSize = 20 };

        // Act
        var result = await _service.GetBalanceDetailsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.All(result.List, item => Assert.Equal(1, item.UserId));
    }

    [Fact]
    public async Task GetBalanceDetails_ShouldFilterByDateRange()
    {
        // Arrange
        SeedUsers(1);
        SeedProfitMoneyWithDates();

        var request = new FinanceQueryRequest
        {
            StartDate = DateTime.Today.AddDays(-3),
            EndDate = DateTime.Today,
            Page = 1,
            PageSize = 20
        };

        // Act
        var result = await _service.GetBalanceDetailsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.All(result.List, item =>
        {
            Assert.True(item.CreatedAt >= request.StartDate.Value.Date);
            Assert.True(item.CreatedAt < request.EndDate.Value.Date.AddDays(1));
        });
    }

    #endregion

    #region 积分明细测试

    [Fact]
    public async Task GetIntegralDetails_ShouldReturnPaginatedResults()
    {
        // Arrange
        SeedUsers(1);
        SeedProfitIntegral(10);

        var request = new FinanceQueryRequest { Page = 1, PageSize = 5 };

        // Act
        var result = await _service.GetIntegralDetailsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(10, result.Total);
        Assert.Equal(5, result.List.Count);
    }

    [Fact]
    public async Task GetIntegralDetails_ShouldFilterByUserId()
    {
        // Arrange
        SeedUsers(2);
        SeedProfitIntegralForMultipleUsers();

        var request = new FinanceQueryRequest { UserId = 2, Page = 1, PageSize = 20 };

        // Act
        var result = await _service.GetIntegralDetailsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.All(result.List, item => Assert.Equal(2, item.UserId));
    }

    #endregion

    #region 钻石明细测试

    [Fact]
    public async Task GetScoreDetails_ShouldReturnPaginatedResults()
    {
        // Arrange
        SeedUsers(1);
        SeedProfitScore(8);

        var request = new FinanceQueryRequest { Page = 1, PageSize = 5 };

        // Act
        var result = await _service.GetScoreDetailsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(8, result.Total);
        Assert.Equal(5, result.List.Count);
    }

    #endregion

    #region 充值记录测试

    [Fact]
    public async Task GetRechargeRecords_ShouldReturnPaginatedResults()
    {
        // Arrange
        SeedUsers(1);
        SeedProfitPay(12);

        var request = new FinanceQueryRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _service.GetRechargeRecordsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(12, result.Total);
        Assert.Equal(10, result.List.Count);
    }

    [Fact]
    public async Task GetRechargeRecords_ShouldIncludePayTypeName()
    {
        // Arrange
        SeedUsers(1);
        SeedProfitPay(1);

        var request = new FinanceQueryRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _service.GetRechargeRecordsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Single(result.List);
        Assert.NotEmpty(result.List[0].PayTypeName);
    }

    #endregion

    #region Helper Methods

    private void SeedUsers(int count)
    {
        for (int i = 1; i <= count; i++)
        {
            _dbContext.Users.Add(new User
            {
                Id = i,
                Uid = $"U{i:D3}",
                Nickname = $"测试用户{i}",
                Mobile = $"1380013800{i}",
                OpenId = $"openid{i}",
                HeadImg = $"http://test.com/head{i}.jpg",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            });
        }
        _dbContext.SaveChanges();
    }

    private void SeedOrders()
    {
        var orders = new[]
        {
            new Order { UserId = 1, OrderNum = "ORD001", Price = 100, UseMoney = 0, UseIntegral = 0, UseScore = 0, Status = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, GoodsId = 1, GoodsTitle = "商品1", GoodsPrice = 100, OrderTotal = 100, OrderZheTotal = 100, Zhe = 1, Num = 1, PrizeNum = 1, Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds() },
            new Order { UserId = 1, OrderNum = "ORD002", Price = 200, UseMoney = 50, UseIntegral = 0, UseScore = 0, Status = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, GoodsId = 1, GoodsTitle = "商品1", GoodsPrice = 200, OrderTotal = 200, OrderZheTotal = 200, Zhe = 1, Num = 1, PrizeNum = 1, Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds() },
            new Order { UserId = 2, OrderNum = "ORD003", Price = 500, UseMoney = 0, UseIntegral = 100, UseScore = 0, Status = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, GoodsId = 1, GoodsTitle = "商品1", GoodsPrice = 500, OrderTotal = 500, OrderZheTotal = 500, Zhe = 1, Num = 1, PrizeNum = 1, Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds() },
            new Order { UserId = 3, OrderNum = "ORD004", Price = 50, UseMoney = 0, UseIntegral = 0, UseScore = 0, Status = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, GoodsId = 1, GoodsTitle = "商品1", GoodsPrice = 50, OrderTotal = 50, OrderZheTotal = 50, Zhe = 1, Num = 1, PrizeNum = 1, Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds() },
        };
        _dbContext.Orders.AddRange(orders);
        _dbContext.SaveChanges();
    }

    private void SeedProfitMoney(int count)
    {
        for (int i = 1; i <= count; i++)
        {
            _dbContext.ProfitMoneys.Add(new ProfitMoney
            {
                UserId = 1,
                ChangeMoney = 100 * i,
                Money = 100 * i,
                Type = 1,
                Content = $"测试变动{i}",
                ShareUid = 0,
                CreatedAt = DateTime.Now
            });
        }
        _dbContext.SaveChanges();
    }

    private void SeedProfitMoneyForMultipleUsers()
    {
        for (int userId = 1; userId <= 3; userId++)
        {
            for (int i = 1; i <= 5; i++)
            {
                _dbContext.ProfitMoneys.Add(new ProfitMoney
                {
                    UserId = userId,
                    ChangeMoney = 100 * i,
                    Money = 100 * i,
                    Type = 1,
                    Content = $"用户{userId}变动{i}",
                    ShareUid = 0,
                    CreatedAt = DateTime.Now
                });
            }
        }
        _dbContext.SaveChanges();
    }

    private void SeedProfitMoneyWithDates()
    {
        for (int i = -10; i <= 0; i++)
        {
            _dbContext.ProfitMoneys.Add(new ProfitMoney
            {
                UserId = 1,
                ChangeMoney = 100,
                Money = 100,
                Type = 1,
                Content = $"日期测试{i}",
                ShareUid = 0,
                CreatedAt = DateTime.Today.AddDays(i)
            });
        }
        _dbContext.SaveChanges();
    }

    private void SeedProfitIntegral(int count)
    {
        for (int i = 1; i <= count; i++)
        {
            _dbContext.ProfitIntegrals.Add(new ProfitIntegral
            {
                UserId = 1,
                ChangeMoney = 50 * i,
                Money = 50 * i,
                Type = 1,
                Content = $"积分变动{i}",
                ShareUid = 0,
                CreatedAt = DateTime.Now
            });
        }
        _dbContext.SaveChanges();
    }

    private void SeedProfitIntegralForMultipleUsers()
    {
        for (int userId = 1; userId <= 2; userId++)
        {
            for (int i = 1; i <= 5; i++)
            {
                _dbContext.ProfitIntegrals.Add(new ProfitIntegral
                {
                    UserId = userId,
                    ChangeMoney = 50 * i,
                    Money = 50 * i,
                    Type = 1,
                    Content = $"用户{userId}积分{i}",
                    ShareUid = 0,
                    CreatedAt = DateTime.Now
                });
            }
        }
        _dbContext.SaveChanges();
    }

    private void SeedProfitScore(int count)
    {
        for (int i = 1; i <= count; i++)
        {
            _dbContext.ProfitScores.Add(new ProfitScore
            {
                UserId = 1,
                ChangeMoney = 10 * i,
                Money = 10 * i,
                Type = 1,
                Content = $"钻石变动{i}",
                ShareUid = 0,
                CreatedAt = DateTime.Now
            });
        }
        _dbContext.SaveChanges();
    }

    private void SeedProfitPay(int count)
    {
        for (int i = 1; i <= count; i++)
        {
            _dbContext.ProfitPays.Add(new ProfitPay
            {
                UserId = 1,
                OrderNum = $"PAY{i:D5}",
                ChangeMoney = 100 * i,
                Content = $"充值{i}",
                PayType = (byte)(i % 4),
                CreatedAt = DateTime.Now
            });
        }
        _dbContext.SaveChanges();
    }

    #endregion
}
