using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using HoneyBox.Admin.Data;
using HoneyBox.Admin.Entities;
using HoneyBox.Admin.Models.Auth;
using HoneyBox.Admin.Models.Common;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;

namespace HoneyBox.Admin.Services;

/// <summary>
/// 认证服务实现
/// </summary>
public class AuthService : IAuthService
{
    private readonly AdminDbContext _dbContext;
    private readonly IConfiguration _configuration;
    private readonly ILogger<AuthService> _logger;
    private readonly ICaptchaService _captchaService;

    // 登录失败锁定配置
    private const int MaxFailedAttempts = 5;
    private const int LockoutMinutes = 30;
    
    // Token配置
    private const int RefreshTokenExpirationDays = 7;
    private const int AccessTokenExpirationMinutes = 30;

    public AuthService(
        AdminDbContext dbContext,
        IConfiguration configuration,
        ILogger<AuthService> logger,
        ICaptchaService captchaService)
    {
        _dbContext = dbContext;
        _configuration = configuration;
        _logger = logger;
        _captchaService = captchaService;
    }

    /// <inheritdoc />
    public async Task<LoginResponse> LoginAsync(LoginRequest request, string ipAddress)
    {
        // 1. 首先验证验证码（优先于密码校验）临时注释，上线在启用
#if !DEBUG
        if (!_captchaService.Validate(request.CaptchaKey, request.CaptchaCode))
        {
            _logger.LogWarning("登录失败：验证码错误或已过期，用户名: {Username}", request.Username);
            throw new AdminException(AdminErrorCodes.CaptchaInvalid, "验证码错误或已过期");
        }
#endif

        // 2. 查找用户
        var user = await _dbContext.AdminUsers
            .Include(u => u.Department)
            .Include(u => u.AdminUserRoles)
                .ThenInclude(ur => ur.Role)
            .FirstOrDefaultAsync(u => u.Username == request.Username);

        if (user == null)
        {
            _logger.LogWarning("登录失败：用户 {Username} 不存在", request.Username);
            throw new AdminException(AdminErrorCodes.InvalidCredentials, "用户名或密码错误");
        }

        // 3. 检查账户是否被锁定
        if (user.LockoutEnd.HasValue && user.LockoutEnd.Value > DateTime.Now)
        {
            var remainingMinutes = (int)(user.LockoutEnd.Value - DateTime.Now).TotalMinutes + 1;
            _logger.LogWarning("登录失败：用户 {Username} 账户已锁定，剩余 {Minutes} 分钟", request.Username, remainingMinutes);
            throw new AdminException(AdminErrorCodes.AccountLocked, $"账户已锁定，请在 {remainingMinutes} 分钟后重试");
        }

        // 4. 检查账户状态
        if (user.Status == 0)
        {
            _logger.LogWarning("登录失败：用户 {Username} 账户已禁用", request.Username);
            throw new AdminException(AdminErrorCodes.AccountDisabled, "账户已禁用");
        }

        // 5. 验证密码
        if (!VerifyPassword(request.Password, user.PasswordHash))
        {
            // 增加失败次数
            user.LoginFailCount++;
            
            // 检查是否需要锁定账户
            if (user.LoginFailCount >= MaxFailedAttempts)
            {
                user.LockoutEnd = DateTime.Now.AddMinutes(LockoutMinutes);
                user.LoginFailCount = 0;
                _logger.LogWarning("用户 {Username} 登录失败次数过多，账户已锁定 {Minutes} 分钟", request.Username, LockoutMinutes);
            }
            
            await _dbContext.SaveChangesAsync();
            
            var remainingAttempts = MaxFailedAttempts - user.LoginFailCount;
            _logger.LogWarning("登录失败：用户 {Username} 密码错误，剩余尝试次数 {Remaining}", request.Username, remainingAttempts);
            throw new AdminException(AdminErrorCodes.InvalidCredentials, "用户名或密码错误");
        }

        // 6. 登录成功，重置失败次数
        user.LoginFailCount = 0;
        user.LockoutEnd = null;
        user.LastLoginTime = DateTime.Now;
        user.LastLoginIp = ipAddress;

        // 7. 生成双Token
        var expireMinutes = _configuration.GetValue<int>("Jwt:ExpireMinutes", AccessTokenExpirationMinutes);
        var accessToken = GenerateJwtToken(user, expireMinutes);
        var (refreshToken, refreshTokenEntity) = GenerateRefreshToken(user.Id, ipAddress);
        
        // 8. 保存RefreshToken到数据库
        _dbContext.RefreshTokens.Add(refreshTokenEntity);
        await _dbContext.SaveChangesAsync();

        // 9. 获取用户角色和权限
        var roles = user.AdminUserRoles
            .Where(ur => ur.Role.Status == 1)
            .Select(ur => ur.Role.Code)
            .ToList();

        var permissions = await GetUserPermissionsAsync(user.Id);

        _logger.LogInformation("用户 {Username} 登录成功，IP: {IpAddress}", request.Username, ipAddress);

        return new LoginResponse
        {
            AccessToken = accessToken,
            RefreshToken = refreshToken,
            ExpiresIn = expireMinutes * 60,
            UserInfo = new AdminUserInfo
            {
                Id = user.Id,
                Username = user.Username,
                RealName = user.RealName,
                Avatar = user.Avatar,
                Email = user.Email,
                Phone = user.Phone,
                DepartmentId = user.DepartmentId,
                DepartmentName = user.Department?.Name,
                Roles = roles,
                Permissions = permissions
            }
        };
    }

    /// <inheritdoc />
    public async Task<RefreshTokenResponse> RefreshTokenAsync(string refreshToken, string ipAddress)
    {
        // 1. 计算Token哈希
        var tokenHash = HashToken(refreshToken);
        
        // 2. 查找Token
        var storedToken = await _dbContext.RefreshTokens
            .Include(t => t.AdminUser)
                .ThenInclude(u => u.AdminUserRoles)
                    .ThenInclude(ur => ur.Role)
            .FirstOrDefaultAsync(t => t.TokenHash == tokenHash);

        if (storedToken == null)
        {
            _logger.LogWarning("Token刷新失败：Token不存在");
            throw new AdminException(AdminErrorCodes.InvalidRefreshToken, "无效的RefreshToken");
        }

        // 3. 检查Token是否有效
        if (!storedToken.IsActive)
        {
            if (storedToken.IsRevoked)
            {
                _logger.LogWarning("Token刷新失败：Token已被撤销，用户ID: {UserId}", storedToken.AdminUserId);
                throw new AdminException(AdminErrorCodes.RefreshTokenRevoked, "RefreshToken已被撤销");
            }
            if (storedToken.IsExpired)
            {
                _logger.LogWarning("Token刷新失败：Token已过期，用户ID: {UserId}", storedToken.AdminUserId);
                throw new AdminException(AdminErrorCodes.RefreshTokenExpired, "RefreshToken已过期");
            }
        }

        // 4. 检查用户状态
        var user = storedToken.AdminUser;
        if (user.Status == 0)
        {
            _logger.LogWarning("Token刷新失败：用户账户已禁用，用户ID: {UserId}", user.Id);
            throw new AdminException(AdminErrorCodes.AccountDisabled, "账户已禁用");
        }

        // 5. 生成新的Token（Token轮换）
        var expireMinutes = _configuration.GetValue<int>("Jwt:ExpireMinutes", AccessTokenExpirationMinutes);
        var newAccessToken = GenerateJwtToken(user, expireMinutes);
        var (newRefreshToken, newRefreshTokenEntity) = GenerateRefreshToken(user.Id, ipAddress);

        // 6. 撤销旧Token并记录替换关系
        storedToken.RevokedAt = DateTime.Now;
        storedToken.RevokedByIp = ipAddress;
        storedToken.ReplacedByToken = newRefreshTokenEntity.TokenHash;

        // 7. 保存新Token
        _dbContext.RefreshTokens.Add(newRefreshTokenEntity);
        await _dbContext.SaveChangesAsync();

        _logger.LogInformation("用户 {UserId} Token刷新成功，IP: {IpAddress}", user.Id, ipAddress);

        return new RefreshTokenResponse
        {
            AccessToken = newAccessToken,
            RefreshToken = newRefreshToken,
            ExpiresIn = expireMinutes * 60
        };
    }

    /// <inheritdoc />
    public async Task RevokeTokenAsync(string refreshToken, string ipAddress)
    {
        // 1. 计算Token哈希
        var tokenHash = HashToken(refreshToken);
        
        // 2. 查找Token
        var storedToken = await _dbContext.RefreshTokens
            .FirstOrDefaultAsync(t => t.TokenHash == tokenHash);

        if (storedToken == null)
        {
            _logger.LogWarning("Token撤销失败：Token不存在");
            throw new AdminException(AdminErrorCodes.InvalidRefreshToken, "无效的RefreshToken");
        }

        // 3. 撤销Token
        if (!storedToken.IsRevoked)
        {
            storedToken.RevokedAt = DateTime.Now;
            storedToken.RevokedByIp = ipAddress;
            await _dbContext.SaveChangesAsync();
        }

        _logger.LogInformation("Token已撤销，用户ID: {UserId}，IP: {IpAddress}", storedToken.AdminUserId, ipAddress);
    }

    /// <inheritdoc />
    public async Task RevokeAllTokensAsync(long adminUserId, string ipAddress)
    {
        // 查找用户所有活跃的Token
        var activeTokens = await _dbContext.RefreshTokens
            .Where(t => t.AdminUserId == adminUserId && t.RevokedAt == null)
            .ToListAsync();

        var now = DateTime.Now;
        foreach (var token in activeTokens)
        {
            token.RevokedAt = now;
            token.RevokedByIp = ipAddress;
        }

        await _dbContext.SaveChangesAsync();

        _logger.LogInformation("用户 {UserId} 的所有Token已撤销（共{Count}个），IP: {IpAddress}", 
            adminUserId, activeTokens.Count, ipAddress);
    }

    /// <inheritdoc />
    public async Task LogoutAsync(long adminUserId)
    {
        // 注意：完整的登出应该通过RevokeTokenAsync来撤销RefreshToken
        // 这里保留原有逻辑，前端应该同时调用RevokeTokenAsync
        _logger.LogInformation("用户 {UserId} 退出登录", adminUserId);
        await Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task<AdminUserInfo> GetCurrentUserInfoAsync(long adminUserId)
    {
        var user = await _dbContext.AdminUsers
            .Include(u => u.Department)
            .Include(u => u.AdminUserRoles)
                .ThenInclude(ur => ur.Role)
            .FirstOrDefaultAsync(u => u.Id == adminUserId);

        if (user == null)
        {
            throw new AdminException(AdminErrorCodes.InvalidCredentials, "用户不存在");
        }

        var roles = user.AdminUserRoles
            .Where(ur => ur.Role.Status == 1)
            .Select(ur => ur.Role.Code)
            .ToList();

        var permissions = await GetUserPermissionsAsync(user.Id);

        return new AdminUserInfo
        {
            Id = user.Id,
            Username = user.Username,
            RealName = user.RealName,
            Avatar = user.Avatar,
            Email = user.Email,
            Phone = user.Phone,
            DepartmentId = user.DepartmentId,
            DepartmentName = user.Department?.Name,
            Roles = roles,
            Permissions = permissions
        };
    }

    /// <inheritdoc />
    public async Task ChangePasswordAsync(long adminUserId, ChangePasswordRequest request)
    {
        var user = await _dbContext.AdminUsers.FindAsync(adminUserId);
        if (user == null)
        {
            throw new AdminException(AdminErrorCodes.InvalidCredentials, "用户不存在");
        }

        // 验证旧密码
        if (!VerifyPassword(request.OldPassword, user.PasswordHash))
        {
            throw new AdminException(AdminErrorCodes.InvalidCredentials, "旧密码错误");
        }

        // 更新密码
        user.PasswordHash = HashPassword(request.NewPassword);
        user.UpdatedAt = DateTime.Now;
        await _dbContext.SaveChangesAsync();

        _logger.LogInformation("用户 {UserId} 修改密码成功", adminUserId);
    }

    /// <summary>
    /// 生成 JWT Token
    /// </summary>
    private string GenerateJwtToken(AdminUser user, int expireMinutes)
    {
        var secret = _configuration["Jwt:Secret"] ?? throw new InvalidOperationException("JWT Secret not configured");
        var issuer = _configuration["Jwt:Issuer"] ?? "HoneyBox.Admin";
        var audience = _configuration["Jwt:Audience"] ?? "HoneyBox.Admin.Client";

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var claims = new List<Claim>
        {
            new(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new(ClaimTypes.Name, user.Username),
            new("real_name", user.RealName ?? ""),
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        // 添加角色声明
        foreach (var userRole in user.AdminUserRoles.Where(ur => ur.Role.Status == 1))
        {
            claims.Add(new Claim(ClaimTypes.Role, userRole.Role.Code));
        }

        var token = new JwtSecurityToken(
            issuer: issuer,
            audience: audience,
            claims: claims,
            expires: DateTime.Now.AddMinutes(expireMinutes),
            signingCredentials: credentials
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    /// <summary>
    /// 生成 Refresh Token
    /// </summary>
    /// <returns>返回原始Token和Token实体</returns>
    private (string Token, RefreshToken Entity) GenerateRefreshToken(long adminUserId, string ipAddress)
    {
        // 生成安全随机Token
        var randomBytes = new byte[64];
        using var rng = RandomNumberGenerator.Create();
        rng.GetBytes(randomBytes);
        var token = Convert.ToBase64String(randomBytes);

        // 创建Token实体
        var refreshToken = new RefreshToken
        {
            AdminUserId = adminUserId,
            TokenHash = HashToken(token),
            ExpiresAt = DateTime.Now.AddDays(RefreshTokenExpirationDays),
            CreatedAt = DateTime.Now,
            CreatedByIp = ipAddress
        };

        return (token, refreshToken);
    }

    /// <summary>
    /// 哈希Token（用于安全存储）
    /// </summary>
    private static string HashToken(string token)
    {
        using var sha256 = SHA256.Create();
        var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(token));
        return Convert.ToBase64String(hashedBytes);
    }

    /// <summary>
    /// 获取用户权限列表
    /// </summary>
    private async Task<List<string>> GetUserPermissionsAsync(long adminUserId)
    {
        // 获取用户的所有角色ID
        var roleIds = await _dbContext.AdminUserRoles
            .Where(ur => ur.AdminUserId == adminUserId)
            .Select(ur => ur.RoleId)
            .ToListAsync();

        // 获取角色关联的权限
        var permissions = await _dbContext.RolePermissions
            .Where(rp => roleIds.Contains(rp.RoleId))
            .Include(rp => rp.Permission)
            .Select(rp => rp.Permission.Code)
            .Distinct()
            .ToListAsync();

        return permissions;
    }

    /// <summary>
    /// 哈希密码
    /// </summary>
    public static string HashPassword(string password)
    {
        using var sha256 = SHA256.Create();
        var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password));
        return Convert.ToBase64String(hashedBytes);
    }

    /// <summary>
    /// 验证密码
    /// </summary>
    private static bool VerifyPassword(string password, string passwordHash)
    {
        var hash = HashPassword(password);
        return hash == passwordHash;
    }
}
