using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using FsCheck;
using FsCheck.Xunit;
using HoneyBox.Core.Services;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Auth;
using Microsoft.Extensions.Logging;
using Moq;

namespace HoneyBox.Tests.Services;

public class JwtServicePropertyTests
{
    private readonly JwtSettings _jwtSettings;
    private readonly Mock<ILogger<JwtService>> _mockLogger;
    private readonly JwtService _jwtService;

    public JwtServicePropertyTests()
    {
        _jwtSettings = new JwtSettings
        {
            Secret = "your-secret-key-must-be-at-least-32-characters-long-for-hs256",
            Issuer = "HoneyBox",
            Audience = "HoneyBoxUsers",
            ExpirationMinutes = 1440,
            RefreshTokenExpirationDays = 7
        };

        _mockLogger = new Mock<ILogger<JwtService>>();
        _jwtService = new JwtService(_jwtSettings, _mockLogger.Object);
    }

    /// <summary>
    /// Property 7: Token验证与授权
    /// For any valid token generated by the service, validation should succeed and return a valid ClaimsPrincipal.
    /// For any invalid token, validation should fail and return null.
    /// Validates: Requirements 3.3, 3.4
    /// </summary>
    [Property(MaxTest = 100)]
    public bool ValidTokenShouldPassValidation(PositiveInt userId, NonEmptyString nickname)
    {
        // Create a valid user and generate a token
        var user = new User
        {
            Id = userId.Item,
            Nickname = nickname.Item,
            Uid = $"uid{userId.Item}",
            OpenId = $"openid{userId.Item}",
            HeadImg = "https://example.com/avatar.jpg",
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow
        };

        var validToken = _jwtService.GenerateToken(user);

        // Valid token should pass validation
        var validPrincipal = _jwtService.ValidateToken(validToken);
        var validTokenPasses = validPrincipal != null;

        // Invalid token should fail validation
        var invalidToken = "invalid.token.string";
        var invalidPrincipal = _jwtService.ValidateToken(invalidToken);
        var invalidTokenFails = invalidPrincipal == null;

        // Null token should fail validation
        var nullPrincipal = _jwtService.ValidateToken(null!);
        var nullTokenFails = nullPrincipal == null;

        return validTokenPasses && invalidTokenFails && nullTokenFails;
    }

    /// <summary>
    /// Property 7 (continued): Token验证与授权 - Claim Extraction
    /// For any valid token, the extracted claims should match the original user data.
    /// Validates: Requirements 3.3, 3.4
    /// </summary>
    [Property(MaxTest = 100)]
    public bool ValidatedTokenShouldContainCorrectClaims(PositiveInt userId, NonEmptyString nickname)
    {
        var user = new User
        {
            Id = userId.Item,
            Nickname = nickname.Item,
            Uid = $"uid{userId.Item}",
            OpenId = $"openid{userId.Item}",
            HeadImg = "https://example.com/avatar.jpg",
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow
        };

        var token = _jwtService.GenerateToken(user);
        var principal = _jwtService.ValidateToken(token);

        if (principal == null)
            return false;

        // Check that the principal contains the correct user ID claim
        var userIdClaim = principal.FindFirst(ClaimTypes.NameIdentifier);
        var userIdMatches = userIdClaim != null && userIdClaim.Value == userId.Item.ToString();

        // Check that the principal contains the correct nickname claim
        var nameClaim = principal.FindFirst(ClaimTypes.Name);
        var nicknameMatches = nameClaim != null && nameClaim.Value == nickname.Item;

        // Check that the principal contains the correct uid claim
        var uidClaim = principal.FindFirst("uid");
        var uidMatches = uidClaim != null && uidClaim.Value == $"uid{userId.Item}";

        return userIdMatches && nicknameMatches && uidMatches;
    }

    /// <summary>
    /// Property 7 (continued): Token验证与授权 - Unauthorized Access
    /// For any request with an invalid or missing token, the system should deny access.
    /// Validates: Requirements 3.3, 3.4
    /// </summary>
    [Property(MaxTest = 100)]
    public bool InvalidTokenShouldDenyAccess(NonEmptyString invalidToken)
    {
        // Any random string should not validate as a token
        var principal = _jwtService.ValidateToken(invalidToken.Item);
        return principal == null;
    }
}
