using Atomx.Common.Entities;
using Atomx.Common.Models;
using Atomx.Data;
using Atomx.Data.CacheServices;
using Atomx.Utils.Extension;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

namespace Atomx.Admin.Services
{
    public interface ITokenService
    {
        Task<AuthResponse> GenerateTokenAsync(User user, string? ipAddress = null, string? userAgent = null);
        Task<AuthResponse> RefreshTokenAsync(string token, string refreshToken, string? ipAddress = null);
        Task<bool> RevokeTokenAsync(string refreshToken, string? ipAddress = null);
        Task<bool> ValidateTokenAsync(string token);
        Task<User?> GetUserFromTokenAsync(string token);
    }

    public class TokenService : ITokenService
    {
        readonly DataContext _dbContext;
        readonly ICacheService _cacheService;
        readonly JwtSetting _jwtSetting;
        private readonly ILogger<TokenService> _logger;
        private readonly SecurityKey _securityKey;
        private readonly SigningCredentials _signingCredentials;

        public TokenService(
            ILogger<TokenService> logger, DataContext dataContext, ICacheService cacheService, JwtSetting jwtSetting)
        {
            _logger = logger;
            _dbContext = dataContext;
            _cacheService = cacheService;
            _jwtSetting = jwtSetting;



            var key = Encoding.UTF8.GetBytes(_jwtSetting.SecurityKey);
            _securityKey = new SymmetricSecurityKey(key);
            _signingCredentials = new SigningCredentials(_securityKey, SecurityAlgorithms.HmacSha256);
        }

        public async Task<AuthResponse> GenerateTokenAsync(User user, string? ipAddress = null, string? userAgent = null)
        {
            if (user == null)
                throw new ArgumentNullException(nameof(user));

            // 检查用户是否被锁定
            if (user.LockoutEndTime.HasValue && user.LockoutEndTime > DateTime.UtcNow)
                throw new InvalidOperationException("账户已被锁定");

            // 生成访问令牌
            var accessToken = GenerateAccessToken(user);

            // 生成刷新令牌
            var refreshToken = GenerateRefreshToken();

            // 保存刷新令牌到数据库
            var refreshTokenEntity = new RefreshToken
            {
                Token = HashRefreshToken(refreshToken),
                UserId = user.Id,
                IssuedTime = DateTime.UtcNow,
                ExpiresTime = DateTime.UtcNow.AddDays(_jwtSetting.RefreshTokenExpirationMinutes),
                Ip = ipAddress,
                UserAgent = userAgent
            };

            // 移除旧的刷新令牌（安全策略）
            await RemoveOldRefreshTokensAsync(user.Id);

            _dbContext.RefreshTokens.Add(refreshTokenEntity);
            await _dbContext.SaveChangesAsync();

            // 记录审计日志
            //await _auditService.LogAsync(new AuditLog
            //{
            //    UserId = user.Id,
            //    Action = "GenerateToken",
            //    Timestamp = DateTime.UtcNow,
            //    IpAddress = ipAddress,
            //    UserAgent = userAgent,
            //    Details = $"Token generated for user {user.Username}"
            //});

            // 缓存令牌（防止重复使用）
            await CacheTokenAsync(accessToken, user.Id);

            return new AuthResponse
            {
                Token = accessToken,
                RefreshToken = refreshToken,
                TokenExpiry = DateTime.UtcNow.AddMinutes(_jwtSetting.AccessTokenExpirationMinutes)
            };
        }

        public async Task<AuthResponse> RefreshTokenAsync(string token, string refreshToken, string? ipAddress = null)
        {
            var principal = GetPrincipalFromExpiredToken(token);
            var userId = principal.FindFirst("sub")?.Value.ToLong();

            if (userId == 0)
                throw new SecurityTokenException("无效的令牌");

            var user = await _dbContext.Users
                .SingleOrDefaultAsync(u => u.Id == userId && u.IsActive);

            if (user == null)
                throw new SecurityTokenException("用户不存在或已被禁用");

            // 验证刷新令牌
            var hashedRefreshToken = HashRefreshToken(refreshToken);
            var storedToken = await _dbContext.RefreshTokens
                .FirstOrDefaultAsync(rt =>
                    rt.Token == hashedRefreshToken &&
                    rt.UserId == userId &&
                    rt.ExpiresTime > DateTime.UtcNow &&
                    !rt.IsRevoked);

            if (storedToken == null)
                throw new SecurityTokenException("无效的刷新令牌");

            // 撤销旧的刷新令牌
            storedToken.IsRevoked = true;
            storedToken.RevokedTime = DateTime.UtcNow;
            storedToken.Ip = ipAddress;

            // 生成新的令牌对
            var newTokenResponse = await GenerateTokenAsync(user, ipAddress, storedToken.UserAgent);

            //// 记录审计日志
            //await _auditService.LogAsync(new AuditLog
            //{
            //    UserId = user.Id,
            //    Action = "RefreshToken",
            //    Timestamp = DateTime.UtcNow,
            //    IpAddress = ipAddress,
            //    Details = "Token refreshed successfully"
            //});

            return newTokenResponse;
        }

        public async Task<bool> RevokeTokenAsync(string refreshToken, string? ipAddress = null)
        {
            var hashedToken = HashRefreshToken(refreshToken);
            var token = await _dbContext.RefreshTokens
                .FirstOrDefaultAsync(rt => rt.Token == hashedToken);

            if (token == null || token.IsRevoked)
                return false;

            token.IsRevoked = true;
            token.RevokedTime = DateTime.UtcNow;
            token.Ip = ipAddress;

            await _dbContext.SaveChangesAsync();

            // 清除缓存
            await _cacheService.Remove($"user:{token.UserId}");

            //await _auditService.LogAsync(new AuditLog
            //{
            //    UserId = token.UserId,
            //    Action = "RevokeToken",
            //    Timestamp = DateTime.UtcNow,
            //    IpAddress = ipAddress,
            //    Details = "Token revoked"
            //});

            return true;
        }

        public async Task<bool> ValidateTokenAsync(string token)
        {
            try
            {
                // 检查令牌是否在缓存中（已被撤销）
                var cacheKey = $"revoked_token:{HashToken(token)}";
                var cached = await _cacheService.GetCacheString(cacheKey);
                if (cached != null)
                    return false;

                var tokenHandler = new JwtSecurityTokenHandler();
                var validationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = _securityKey,
                    ValidateIssuer = true,
                    ValidIssuer = _jwtSetting.Issuer,
                    ValidateAudience = true,
                    ValidAudience = _jwtSetting.Audience,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                };

                tokenHandler.ValidateToken(token, validationParameters, out _);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public async Task<User?> GetUserFromTokenAsync(string token)
        {
            try
            {
                var principal = GetPrincipalFromToken(token);
                var userId = principal.FindFirst("sub")?.Value.ToLong();

                if (userId == 0)
                    return null;

                // 尝试从缓存获取
                var cacheKey = $"user:{userId}";
                var cachedUser = await _cacheService.GetCacheString(cacheKey);

                if (!string.IsNullOrEmpty(cachedUser))
                {
                    return JsonSerializer.Deserialize<User>(cachedUser);
                }

                // 从数据库获取
                var user = await _dbContext.Users
                    .FirstOrDefaultAsync(u => u.Id == userId && u.IsActive);

                if (user != null)
                {
                    // 缓存用户信息（5分钟）
                    await _cacheService.SetCacheAsync(cacheKey, JsonSerializer.Serialize(user), 5);
                }

                return user;
            }
            catch
            {
                return null;
            }
        }

        private string GenerateAccessToken(User user)
        {
            var claims = new[]
            {
            new Claim("sub", user.Id.ToString()),
            new Claim("jti", Guid.NewGuid().ToString()),
            new Claim("name", user.Name),
            new Claim("email", user.Email),
            new Claim("email_confirmed", user.EmailConfirmed.ToString().ToLower()),
            new Claim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
        }.ToList();

            // 添加角色声明
            //foreach (var userRole in user.UserRoles)
            //{
            //    claims.Add(new Claim("role", userRole.Role.ToString()));
            //}

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(claims),
                Expires = DateTime.UtcNow.AddMinutes(_jwtSetting.AccessTokenExpirationMinutes),
                Issuer = _jwtSetting.Issuer,
                Audience = _jwtSetting.Audience,
                SigningCredentials = _signingCredentials,
                NotBefore = DateTime.UtcNow
            };

            var tokenHandler = new JwtSecurityTokenHandler();
            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

        private string GenerateRefreshToken()
        {
            var randomNumber = new byte[64];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }

        private ClaimsPrincipal GetPrincipalFromToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();

            try
            {
                var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = _securityKey,
                    ValidateIssuer = true,
                    ValidIssuer = _jwtSetting.Issuer,
                    ValidateAudience = true,
                    ValidAudience = _jwtSetting.Audience,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                }, out _);

                return principal;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "令牌验证失败");
                throw new SecurityTokenException("无效的令牌", ex);
            }
        }

        private ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();

            try
            {
                var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = _securityKey,
                    ValidateIssuer = true,
                    ValidIssuer = _jwtSetting.Issuer,
                    ValidateAudience = true,
                    ValidAudience = _jwtSetting.Audience,
                    ValidateLifetime = false, // 注意：这里不验证过期时间
                    ClockSkew = TimeSpan.Zero
                }, out _);

                return principal;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "过期令牌验证失败");
                throw new SecurityTokenException("无效的令牌", ex);
            }
        }

        private string HashRefreshToken(string refreshToken)
        {
            using var sha256 = SHA256.Create();
            var bytes = Encoding.UTF8.GetBytes(refreshToken + _jwtSetting.SecurityKey);
            var hash = sha256.ComputeHash(bytes);
            return Convert.ToBase64String(hash);
        }

        private string HashToken(string token)
        {
            using var sha256 = SHA256.Create();
            var bytes = Encoding.UTF8.GetBytes(token);
            var hash = sha256.ComputeHash(bytes);
            return Convert.ToBase64String(hash);
        }

        private async Task RemoveOldRefreshTokensAsync(long userId)
        {
            var tokens = await _dbContext.RefreshTokens
                .Where(rt => rt.UserId == userId && !rt.IsRevoked)
                .OrderByDescending(rt => rt.ExpiresTime)
                .Skip(_jwtSetting.MaxRefreshTokensPerUser - 1)
                .ToListAsync();

            foreach (var token in tokens)
            {
                token.IsRevoked = true;
                token.RevokedTime = DateTime.UtcNow;
            }
        }

        private async Task CacheTokenAsync(string token, long userId)
        {
            var cacheKey = $"token:{HashToken(token)}";
            await _cacheService.SetCacheAsync(cacheKey, userId, _jwtSetting.AccessTokenExpirationMinutes);
        }
    }
}
