using FsCheck;
using FsCheck.Xunit;
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>
/// DashboardService 属性测试
/// </summary>
public class DashboardServicePropertyTests
{
    private readonly Mock<ILogger<DashboardService>> _mockLogger = new();

    #region Property 15: Dashboard Statistics Accuracy

    /// <summary>
    /// **Feature: admin-business-migration, Property 15: Dashboard Statistics Accuracy**
    /// For any dashboard overview request, the today's registrations count should match 
    /// the actual count of users registered today.
    /// Validates: Requirements 8.1
    /// </summary>
    [Property(MaxTest = 100)]
    public bool DashboardOverview_TodayRegistrations_ShouldMatchActualCount(PositiveInt todayCount, PositiveInt yesterdayCount)
    {
        var actualTodayCount = todayCount.Get % 20; // 0-19 users today
        var actualYesterdayCount = yesterdayCount.Get % 20; // 0-19 users yesterday

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

        using var dbContext = new HoneyBoxDbContext(options);
        var service = new DashboardService(dbContext, _mockLogger.Object);

        // Seed today's users
        var today = DateTime.Today;
        for (int i = 1; i <= actualTodayCount; i++)
        {
            dbContext.Users.Add(new User
            {
                Id = i,
                Uid = $"TODAY{i:D3}",
                Nickname = $"今日用户{i}",
                Mobile = $"1380013800{i}",
                OpenId = $"openid_today_{i}",
                HeadImg = $"http://test.com/head{i}.jpg",
                CreatedAt = today.AddHours(i % 24),
                UpdatedAt = DateTime.Now
            });
        }

        // Seed yesterday's users
        for (int i = 1; i <= actualYesterdayCount; i++)
        {
            var id = actualTodayCount + i;
            dbContext.Users.Add(new User
            {
                Id = id,
                Uid = $"YEST{i:D3}",
                Nickname = $"昨日用户{i}",
                Mobile = $"1390013900{i}",
                OpenId = $"openid_yest_{i}",
                HeadImg = $"http://test.com/head{id}.jpg",
                CreatedAt = today.AddDays(-1).AddHours(i % 24),
                UpdatedAt = DateTime.Now
            });
        }
        dbContext.SaveChanges();

        // Get dashboard overview
        var result = service.GetOverviewAsync().GetAwaiter().GetResult();

        // Verify today's registrations count
        return result.TodayRegistrations == actualTodayCount;
    }

    /// <summary>
    /// **Feature: admin-business-migration, Property 15: Dashboard Statistics Accuracy**
    /// For any dashboard overview request, the today's consumption should match 
    /// the sum of prices from today's paid orders.
    /// Validates: Requirements 8.2
    /// </summary>
    [Property(MaxTest = 100)]
    public bool DashboardOverview_TodayConsumption_ShouldMatchActualSum(PositiveInt todayOrderCount, PositiveInt yesterdayOrderCount)
    {
        var actualTodayCount = todayOrderCount.Get % 15; // 0-14 orders today
        var actualYesterdayCount = yesterdayOrderCount.Get % 15; // 0-14 orders yesterday

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

        using var dbContext = new HoneyBoxDbContext(options);
        var service = new DashboardService(dbContext, _mockLogger.Object);

        // Seed a user
        dbContext.Users.Add(new User
        {
            Id = 1,
            Uid = "U001",
            Nickname = "测试用户",
            Mobile = "13800138001",
            OpenId = "openid1",
            HeadImg = "http://test.com/head.jpg",
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        });
        dbContext.SaveChanges();

        // Seed today's orders
        var today = DateTime.Today;
        decimal expectedTodayConsumption = 0;
        for (int i = 1; i <= actualTodayCount; i++)
        {
            var price = 100 * i;
            expectedTodayConsumption += price;
            dbContext.Orders.Add(CreateOrder(1, $"TODAY{i:D3}", price, today.AddHours(i % 24)));
        }

        // Seed yesterday's orders
        for (int i = 1; i <= actualYesterdayCount; i++)
        {
            var price = 200 * i;
            dbContext.Orders.Add(CreateOrder(1, $"YEST{i:D3}", price, today.AddDays(-1).AddHours(i % 24)));
        }
        dbContext.SaveChanges();

        // Get dashboard overview
        var result = service.GetOverviewAsync().GetAwaiter().GetResult();

        // Verify today's consumption
        return result.TodayConsumption == expectedTodayConsumption;
    }

    /// <summary>
    /// **Feature: admin-business-migration, Property 15: Dashboard Statistics Accuracy**
    /// For any dashboard overview request, the total users count should match 
    /// the actual count of all users in the database.
    /// Validates: Requirements 8.1
    /// </summary>
    [Property(MaxTest = 100)]
    public bool DashboardOverview_TotalUsers_ShouldMatchActualCount(PositiveInt userCount)
    {
        var actualUserCount = (userCount.Get % 50) + 1; // 1-50 users

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

        using var dbContext = new HoneyBoxDbContext(options);
        var service = new DashboardService(dbContext, _mockLogger.Object);

        // Seed users
        for (int i = 1; i <= actualUserCount; i++)
        {
            dbContext.Users.Add(new User
            {
                Id = i,
                Uid = $"U{i:D3}",
                Nickname = $"用户{i}",
                Mobile = $"1380013800{i % 10}",
                OpenId = $"openid{i}",
                HeadImg = $"http://test.com/head{i}.jpg",
                CreatedAt = DateTime.Now.AddDays(-i % 30),
                UpdatedAt = DateTime.Now
            });
        }
        dbContext.SaveChanges();

        // Get dashboard overview
        var result = service.GetOverviewAsync().GetAwaiter().GetResult();

        // Verify total users count
        return result.TotalUsers == actualUserCount;
    }

    /// <summary>
    /// **Feature: admin-business-migration, Property 15: Dashboard Statistics Accuracy**
    /// For any dashboard overview request, the total orders count should match 
    /// the actual count of paid orders in the database.
    /// Validates: Requirements 8.2
    /// </summary>
    [Property(MaxTest = 100)]
    public bool DashboardOverview_TotalOrders_ShouldMatchActualCount(PositiveInt paidCount, PositiveInt unpaidCount)
    {
        var actualPaidCount = paidCount.Get % 30; // 0-29 paid orders
        var actualUnpaidCount = unpaidCount.Get % 20; // 0-19 unpaid orders

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

        using var dbContext = new HoneyBoxDbContext(options);
        var service = new DashboardService(dbContext, _mockLogger.Object);

        // Seed a user
        dbContext.Users.Add(new User
        {
            Id = 1,
            Uid = "U001",
            Nickname = "测试用户",
            Mobile = "13800138001",
            OpenId = "openid1",
            HeadImg = "http://test.com/head.jpg",
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        });
        dbContext.SaveChanges();

        // Seed paid orders (status = 1)
        for (int i = 1; i <= actualPaidCount; i++)
        {
            dbContext.Orders.Add(CreateOrder(1, $"PAID{i:D3}", 100, DateTime.Now, 1));
        }

        // Seed unpaid orders (status = 0)
        for (int i = 1; i <= actualUnpaidCount; i++)
        {
            dbContext.Orders.Add(CreateOrder(1, $"UNPAID{i:D3}", 100, DateTime.Now, 0));
        }
        dbContext.SaveChanges();

        // Get dashboard overview
        var result = service.GetOverviewAsync().GetAwaiter().GetResult();

        // Verify total orders count (only paid orders)
        return result.TotalOrders == actualPaidCount;
    }

    #endregion

    #region Helper Methods

    private Order CreateOrder(int userId, string orderNum, decimal price, DateTime createdAt, byte status = 1)
    {
        return new Order
        {
            UserId = userId,
            OrderNum = orderNum,
            Price = price,
            UseMoney = 0,
            UseIntegral = 0,
            UseScore = 0,
            Status = status,
            CreatedAt = createdAt,
            UpdatedAt = DateTime.Now,
            GoodsId = 1,
            GoodsTitle = "商品",
            GoodsPrice = price,
            OrderTotal = price,
            OrderZheTotal = price,
            Zhe = 1,
            Num = 1,
            PrizeNum = 1,
            Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds()
        };
    }

    #endregion
}
