using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Models.Enums;
using MilitaryTrainingManagement.Services.Implementations;
using System.IdentityModel.Tokens.Jwt;

namespace MilitaryTrainingManagement.Tests.Services;

/// <summary>
/// 认证服务单元测试
/// 测试JWT令牌生成和验证
/// 测试权限检查逻辑
/// _需求：1.1, 1.3_
/// </summary>
public class AuthenticationServiceTests : IDisposable
{
    private readonly ApplicationDbContext _context;
    private readonly AuthenticationService _service;
    private readonly IConfiguration _configuration;

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

        // 配置JWT设置
        var configValues = new Dictionary<string, string?>
        {
            { "Jwt:Key", "ThisIsAVeryLongSecretKeyForTestingPurposes123456" },
            { "Jwt:Issuer", "TestIssuer" },
            { "Jwt:Audience", "TestAudience" },
            { "Jwt:ExpirationInMinutes", "60" }
        };
        _configuration = new ConfigurationBuilder()
            .AddInMemoryCollection(configValues)
            .Build();

        _service = new AuthenticationService(_context, _configuration);
    }

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

    private async Task<OrganizationalUnit> CreateTestOrganizationalUnit()
    {
        var unit = new OrganizationalUnit
        {
            Name = "测试师团",
            Level = OrganizationalLevel.Division,
            CreatedAt = DateTime.UtcNow
        };
        _context.OrganizationalUnits.Add(unit);
        await _context.SaveChangesAsync();
        return unit;
    }

    #region 密码哈希测试

    [Fact]
    public void HashPassword_ShouldReturnConsistentHash()
    {
        // Arrange
        var password = "TestPassword123";

        // Act
        var hash1 = _service.HashPassword(password);
        var hash2 = _service.HashPassword(password);

        // Assert
        Assert.Equal(hash1, hash2);
    }

    [Fact]
    public void HashPassword_DifferentPasswords_ShouldReturnDifferentHashes()
    {
        // Arrange
        var password1 = "Password1";
        var password2 = "Password2";

        // Act
        var hash1 = _service.HashPassword(password1);
        var hash2 = _service.HashPassword(password2);

        // Assert
        Assert.NotEqual(hash1, hash2);
    }

    [Fact]
    public void ValidatePassword_CorrectPassword_ShouldReturnTrue()
    {
        // Arrange
        var password = "TestPassword123";
        var hash = _service.HashPassword(password);

        // Act
        var result = _service.ValidatePassword(password, hash);

        // Assert
        Assert.True(result);
    }

    [Fact]
    public void ValidatePassword_IncorrectPassword_ShouldReturnFalse()
    {
        // Arrange
        var password = "TestPassword123";
        var wrongPassword = "WrongPassword";
        var hash = _service.HashPassword(password);

        // Act
        var result = _service.ValidatePassword(wrongPassword, hash);

        // Assert
        Assert.False(result);
    }

    #endregion

    #region JWT令牌生成测试

    [Fact]
    public async Task GenerateJwtToken_ShouldReturnValidToken()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        var user = new UserAccount
        {
            Id = 1,
            Username = "testuser",
            DisplayName = "测试用户",
            OrganizationalUnitId = unit.Id,
            OrganizationalUnit = unit,
            IsActive = true
        };

        // Act
        var token = _service.GenerateJwtToken(user);

        // Assert
        Assert.NotNull(token);
        Assert.NotEmpty(token);
    }

    [Fact]
    public async Task GenerateJwtToken_ShouldContainCorrectClaims()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        var user = new UserAccount
        {
            Id = 42,
            Username = "testuser",
            DisplayName = "测试用户",
            OrganizationalUnitId = unit.Id,
            OrganizationalUnit = unit,
            IsActive = true
        };

        // Act
        var token = _service.GenerateJwtToken(user);
        var handler = new JwtSecurityTokenHandler();
        var jwtToken = handler.ReadJwtToken(token);

        // Assert
        Assert.Equal("42", jwtToken.Claims.First(c => c.Type == "userId").Value);
        Assert.Equal("testuser", jwtToken.Claims.First(c => c.Type == "username").Value);
        Assert.Equal(unit.Id.ToString(), jwtToken.Claims.First(c => c.Type == "unitId").Value);
        Assert.Equal(((int)unit.Level).ToString(), jwtToken.Claims.First(c => c.Type == "unitLevel").Value);
    }

    [Fact]
    public async Task GenerateJwtToken_ShouldHaveCorrectExpiration()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        var user = new UserAccount
        {
            Id = 1,
            Username = "testuser",
            DisplayName = "测试用户",
            OrganizationalUnitId = unit.Id,
            OrganizationalUnit = unit,
            IsActive = true
        };

        // Act
        var token = _service.GenerateJwtToken(user);
        var handler = new JwtSecurityTokenHandler();
        var jwtToken = handler.ReadJwtToken(token);

        // Assert - 令牌应该在60分钟后过期（配置中设置）
        var expectedExpiration = DateTime.UtcNow.AddMinutes(60);
        Assert.True(jwtToken.ValidTo > DateTime.UtcNow);
        Assert.True(jwtToken.ValidTo <= expectedExpiration.AddSeconds(5)); // 允许5秒误差
    }

    #endregion

    #region 用户创建测试

    [Fact]
    public async Task CreateUserAsync_ShouldCreateUserWithHashedPassword()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        var username = "newuser";
        var password = "Password123";
        var displayName = "新用户";

        // Act
        var user = await _service.CreateUserAsync(username, password, displayName, unit.Id);

        // Assert
        Assert.NotNull(user);
        Assert.Equal(username, user.Username);
        Assert.Equal(displayName, user.DisplayName);
        Assert.Equal(unit.Id, user.OrganizationalUnitId);
        Assert.True(user.IsActive);
        Assert.NotEqual(password, user.PasswordHash); // 密码应该被哈希
        Assert.True(_service.ValidatePassword(password, user.PasswordHash));
    }

    #endregion

    #region 登录测试

    [Fact]
    public async Task LoginAsync_ValidCredentials_ShouldReturnUserAndToken()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        var password = "Password123";
        await _service.CreateUserAsync("loginuser", password, "登录用户", unit.Id);

        // Act
        var (user, token) = await _service.LoginAsync("loginuser", password);

        // Assert
        Assert.NotNull(user);
        Assert.NotNull(token);
        Assert.Equal("loginuser", user.Username);
    }

    [Fact]
    public async Task LoginAsync_InvalidUsername_ShouldReturnNull()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        await _service.CreateUserAsync("existinguser", "Password123", "现有用户", unit.Id);

        // Act
        var (user, token) = await _service.LoginAsync("nonexistent", "Password123");

        // Assert
        Assert.Null(user);
        Assert.Null(token);
    }

    [Fact]
    public async Task LoginAsync_InvalidPassword_ShouldReturnNull()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        await _service.CreateUserAsync("testuser", "CorrectPassword", "测试用户", unit.Id);

        // Act
        var (user, token) = await _service.LoginAsync("testuser", "WrongPassword");

        // Assert
        Assert.Null(user);
        Assert.Null(token);
    }

    [Fact]
    public async Task LoginAsync_InactiveUser_ShouldReturnNull()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        var user = await _service.CreateUserAsync("inactiveuser", "Password123", "非活跃用户", unit.Id);
        user.IsActive = false;
        await _context.SaveChangesAsync();

        // Act
        var (resultUser, token) = await _service.LoginAsync("inactiveuser", "Password123");

        // Assert
        Assert.Null(resultUser);
        Assert.Null(token);
    }

    [Fact]
    public async Task LoginAsync_ShouldUpdateLastLoginTime()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        var password = "Password123";
        var createdUser = await _service.CreateUserAsync("timeuser", password, "时间用户", unit.Id);
        var beforeLogin = DateTime.UtcNow;

        // Act
        await _service.LoginAsync("timeuser", password);
        var updatedUser = await _service.GetUserByIdAsync(createdUser.Id);

        // Assert
        Assert.NotNull(updatedUser?.LastLoginAt);
        Assert.True(updatedUser.LastLoginAt >= beforeLogin);
    }

    #endregion

    #region 用户查询测试

    [Fact]
    public async Task GetUserByIdAsync_ExistingUser_ShouldReturnUser()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        var createdUser = await _service.CreateUserAsync("finduser", "Password123", "查找用户", unit.Id);

        // Act
        var user = await _service.GetUserByIdAsync(createdUser.Id);

        // Assert
        Assert.NotNull(user);
        Assert.Equal(createdUser.Id, user.Id);
        Assert.Equal("finduser", user.Username);
    }

    [Fact]
    public async Task GetUserByIdAsync_NonExistingUser_ShouldReturnNull()
    {
        // Act
        var user = await _service.GetUserByIdAsync(999);

        // Assert
        Assert.Null(user);
    }

    [Fact]
    public async Task GetUserByUsernameAsync_ExistingUser_ShouldReturnUser()
    {
        // Arrange
        var unit = await CreateTestOrganizationalUnit();
        await _service.CreateUserAsync("uniqueuser", "Password123", "唯一用户", unit.Id);

        // Act
        var user = await _service.GetUserByUsernameAsync("uniqueuser");

        // Assert
        Assert.NotNull(user);
        Assert.Equal("uniqueuser", user.Username);
    }

    [Fact]
    public async Task GetUserByUsernameAsync_NonExistingUser_ShouldReturnNull()
    {
        // Act
        var user = await _service.GetUserByUsernameAsync("nonexistent");

        // Assert
        Assert.Null(user);
    }

    #endregion
}
