using FsCheck;
using FsCheck.Xunit;
using HoneyBox.Core.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Auth;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace HoneyBox.Tests.Services;

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

        return new HoneyBoxDbContext(options);
    }

    private UserService CreateUserService(HoneyBoxDbContext dbContext)
    {
        var mockLogger = new Mock<ILogger<UserService>>();
        return new UserService(dbContext, mockLogger.Object);
    }

    /// <summary>
    /// Property 2: 新用户创建完整性
    /// For any valid CreateUserDto, the created user should have all required fields populated:
    /// - Valid uid (non-empty)
    /// - Non-empty nickname
    /// - Valid headimg URL
    /// - Correct pid if provided
    /// Validates: Requirements 1.3, 2.3
    /// </summary>
    [Property(MaxTest = 100)]
    public async Task<bool> CreatedUserHasAllRequiredFields()
    {
        var dbContext = CreateInMemoryDbContext();
        var userService = CreateUserService(dbContext);

        var createDto = new CreateUserDto
        {
            OpenId = "openid_" + Guid.NewGuid().ToString().Substring(0, 8),
            Nickname = "TestUser_" + Random.Shared.Next(1000, 9999),
            Headimg = "https://example.com/avatar.jpg",
            Pid = 0,
            ClickId = null
        };

        var createdUser = await userService.CreateUserAsync(createDto);

        // Verify all required fields are populated
        var hasValidUid = !string.IsNullOrWhiteSpace(createdUser.Uid);
        var hasNickname = !string.IsNullOrWhiteSpace(createdUser.Nickname);
        var hasHeadImg = !string.IsNullOrWhiteSpace(createdUser.HeadImg);
        var hasCorrectPid = createdUser.Pid == 0;

        return hasValidUid && hasNickname && hasHeadImg && hasCorrectPid;
    }

    /// <summary>
    /// Property 9: 用户信息完整性
    /// For any user, GetUserInfoAsync should return complete information including:
    /// - All required fields (id, uid, nickname, headimg, mobile, money, integral, vip)
    /// - Masked mobile number in format XXX****XXXX
    /// - Correct MobileIs flag (0 if no mobile, 1 if mobile exists)
    /// Validates: Requirements 4.1, 4.4
    /// </summary>
    [Property(MaxTest = 100)]
    public async Task<bool> UserInfoIsComplete(NonEmptyString openId)
    {
        var dbContext = CreateInMemoryDbContext();
        var userService = CreateUserService(dbContext);

        var createDto = new CreateUserDto
        {
            OpenId = openId.Item,
            Nickname = "TestUser",
            Headimg = "https://example.com/avatar.jpg",
            Mobile = "13812345678",
            Pid = 0
        };

        var createdUser = await userService.CreateUserAsync(createDto);
        var userInfo = await userService.GetUserInfoAsync(createdUser.Id);

        // Verify all required fields are present
        var hasId = userInfo?.Id > 0;
        var hasUid = !string.IsNullOrWhiteSpace(userInfo?.Uid);
        var hasNickname = !string.IsNullOrWhiteSpace(userInfo?.Nickname);
        var hasHeadimg = !string.IsNullOrWhiteSpace(userInfo?.Headimg);
        var hasMobileInfo = userInfo?.MobileIs == 1;
        var hasMaskedMobile = userInfo?.Mobile != null && userInfo.Mobile.Contains("****");

        return hasId && hasUid && hasNickname && hasHeadimg && hasMobileInfo && hasMaskedMobile;
    }

    /// <summary>
    /// Property 10: 昵称更新验证
    /// For any user, updating with a valid (non-empty) nickname should succeed,
    /// and updating with empty/whitespace nickname should not change the nickname.
    /// Validates: Requirements 4.2
    /// </summary>
    [Property(MaxTest = 100)]
    public async Task<bool> NicknameUpdateValidation()
    {
        var dbContext = CreateInMemoryDbContext();
        var userService = CreateUserService(dbContext);

        var createDto = new CreateUserDto
        {
            OpenId = "openid_" + Guid.NewGuid().ToString().Substring(0, 8),
            Nickname = "OriginalNickname",
            Headimg = "https://example.com/avatar.jpg",
            Pid = 0
        };

        var createdUser = await userService.CreateUserAsync(createDto);
        var originalNickname = createdUser.Nickname;

        // Update with valid nickname
        var newNickname = "UpdatedNickname_" + Random.Shared.Next(1000, 9999);
        var updateDto = new UpdateUserDto
        {
            Nickname = newNickname
        };

        await userService.UpdateUserAsync(createdUser.Id, updateDto);

        var updatedUser = await userService.GetUserByIdAsync(createdUser.Id);

        // Verify nickname was updated
        return updatedUser?.Nickname == newNickname && updatedUser.Nickname != originalNickname;
    }

    /// <summary>
    /// Property 11: VIP等级计算
    /// For any user with a given spending amount, CalculateVipLevelAsync should return
    /// a VIP level that matches the spending threshold configured in EquityLevel table.
    /// Validates: Requirements 4.5
    /// </summary>
    [Property(MaxTest = 100)]
    public async Task<bool> VipLevelCalculationIsCorrect(PositiveInt spendingAmount)
    {
        var dbContext = CreateInMemoryDbContext();
        var userService = CreateUserService(dbContext);

        // Setup equity levels
        var equityLevels = new[]
        {
            new EquityLevel { Level = 1, Title = "Bronze", Number = 100, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow },
            new EquityLevel { Level = 2, Title = "Silver", Number = 500, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow },
            new EquityLevel { Level = 3, Title = "Gold", Number = 1000, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }
        };

        await dbContext.EquityLevels.AddRangeAsync(equityLevels);
        await dbContext.SaveChangesAsync();

        // Create user with spending
        var createDto = new CreateUserDto
        {
            OpenId = "openid123",
            Nickname = "TestUser",
            Headimg = "https://example.com/avatar.jpg",
            Pid = 0
        };

        var createdUser = await userService.CreateUserAsync(createDto);
        createdUser.Money = spendingAmount.Item;
        dbContext.Users.Update(createdUser);
        await dbContext.SaveChangesAsync();

        // Calculate VIP level
        var calculatedVip = await userService.CalculateVipLevelAsync(createdUser.Id, 0);

        // Verify VIP level matches spending
        int expectedVip = 0;
        if (spendingAmount.Item >= 1000) expectedVip = 3;
        else if (spendingAmount.Item >= 500) expectedVip = 2;
        else if (spendingAmount.Item >= 100) expectedVip = 1;

        return calculatedVip == expectedVip;
    }
}
