package com.dora.service;

import com.dora.config.JwtConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * JWT Token 管理服务
 * 负责管理用户的活跃token，实现设备数量限制
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class JwtTokenManager {
    
    private final RedisTemplate<String, String> redisTemplate;
    private final JwtConfig jwtConfig;
    
    private static final String USER_TOKENS_PREFIX = "user_tokens:";
    private static final String ADMIN_TOKENS_PREFIX = "admin_tokens:";
    
    /**
     * 添加用户token
     * @param userId 用户ID
     * @param tokenId JWT ID
     * @param createTime token创建时间戳
     */
    public void addUserToken(Long userId, String tokenId, long createTime) {
        String key = USER_TOKENS_PREFIX + userId;
        
        try {
            // 添加新token到ZSet，score为创建时间戳
            redisTemplate.opsForZSet().add(key, tokenId, createTime);
            
            // 设置过期时间为JWT过期时间的2倍，确保有足够时间清理
            redisTemplate.expire(key, jwtConfig.getExpiration() * 2, TimeUnit.MILLISECONDS);
            
            // 检查并强制执行设备数量限制
            enforceUserDeviceLimit(userId);
            
            log.debug("用户token已添加 - userId: {}, tokenId: {}", userId, tokenId);
            
        } catch (Exception e) {
            log.error("添加用户token失败 - userId: {}, tokenId: {}", userId, tokenId, e);
        }
    }
    
    /**
     * 添加管理员token
     * @param adminId 管理员ID
     * @param tokenId JWT ID
     * @param createTime token创建时间戳
     */
    public void addAdminToken(Long adminId, String tokenId, long createTime) {
        String key = ADMIN_TOKENS_PREFIX + adminId;
        
        try {
            // 添加新token到ZSet
            redisTemplate.opsForZSet().add(key, tokenId, createTime);
            
            // 设置过期时间
            redisTemplate.expire(key, jwtConfig.getExpiration() * 2, TimeUnit.MILLISECONDS);
            
            // 管理员暂不限制设备数，可根据需要调整
            log.debug("管理员token已添加 - adminId: {}, tokenId: {}", adminId, tokenId);
            
        } catch (Exception e) {
            log.error("添加管理员token失败 - adminId: {}, tokenId: {}", adminId, tokenId, e);
        }
    }
    
    /**
     * 移除用户token
     * @param userId 用户ID
     * @param tokenId JWT ID
     */
    public void removeUserToken(Long userId, String tokenId) {
        String key = USER_TOKENS_PREFIX + userId;
        
        try {
            Long removed = redisTemplate.opsForZSet().remove(key, tokenId);
            log.debug("用户token已移除 - userId: {}, tokenId: {}, removed: {}", userId, tokenId, removed);
            
        } catch (Exception e) {
            log.error("移除用户token失败 - userId: {}, tokenId: {}", userId, tokenId, e);
        }
    }
    
    /**
     * 移除管理员token
     * @param adminId 管理员ID
     * @param tokenId JWT ID
     */
    public void removeAdminToken(Long adminId, String tokenId) {
        String key = ADMIN_TOKENS_PREFIX + adminId;
        
        try {
            Long removed = redisTemplate.opsForZSet().remove(key, tokenId);
            log.debug("管理员token已移除 - adminId: {}, tokenId: {}, removed: {}", adminId, tokenId, removed);
            
        } catch (Exception e) {
            log.error("移除管理员token失败 - adminId: {}, tokenId: {}", adminId, tokenId, e);
        }
    }
    
    /**
     * 检查用户token是否有效
     * @param userId 用户ID
     * @param tokenId JWT ID
     * @return 是否有效
     */
    public boolean isUserTokenValid(Long userId, String tokenId) {
        String key = USER_TOKENS_PREFIX + userId;
        
        try {
            Double score = redisTemplate.opsForZSet().score(key, tokenId);
            boolean valid = score != null;
            
            log.debug("用户token验证 - userId: {}, tokenId: {}, valid: {}", userId, tokenId, valid);
            return valid;
            
        } catch (Exception e) {
            log.error("验证用户token失败 - userId: {}, tokenId: {}", userId, tokenId, e);
            // Redis异常时采用宽松策略，允许访问
            return true;
        }
    }
    
    /**
     * 检查管理员token是否有效
     * @param adminId 管理员ID
     * @param tokenId JWT ID
     * @return 是否有效
     */
    public boolean isAdminTokenValid(Long adminId, String tokenId) {
        String key = ADMIN_TOKENS_PREFIX + adminId;
        
        try {
            Double score = redisTemplate.opsForZSet().score(key, tokenId);
            boolean valid = score != null;
            
            log.debug("管理员token验证 - adminId: {}, tokenId: {}, valid: {}", adminId, tokenId, valid);
            return valid;
            
        } catch (Exception e) {
            log.error("验证管理员token失败 - adminId: {}, tokenId: {}", adminId, tokenId, e);
            // Redis异常时采用宽松策略，允许访问
            return true;
        }
    }
    
    /**
     * 强制执行用户设备数量限制
     * @param userId 用户ID
     */
    private void enforceUserDeviceLimit(Long userId) {
        String key = USER_TOKENS_PREFIX + userId;
        Integer deviceLimit = jwtConfig.getDeviceLimit();
        
        try {
            // 获取当前token数量
            Long currentCount = redisTemplate.opsForZSet().zCard(key);
            
            if (currentCount != null && currentCount > deviceLimit) {
                // 超过限制，删除最旧的token
                long removeCount = currentCount - deviceLimit;
                
                // 获取最旧的token并删除
                Set<String> oldestTokens = redisTemplate.opsForZSet()
                        .range(key, 0, removeCount - 1);
                
                if (oldestTokens != null && !oldestTokens.isEmpty()) {
                    // 批量删除最旧的token
                    redisTemplate.opsForZSet().remove(key, oldestTokens.toArray());
                    
                    log.info("用户设备数限制执行完成 - userId: {}, 删除最旧token数量: {}, 剩余token数量: {}", 
                            userId, oldestTokens.size(), deviceLimit);
                }
            }
            
        } catch (Exception e) {
            log.error("强制执行用户设备数限制失败 - userId: {}", userId, e);
        }
    }
    
    /**
     * 清理过期的token（由定时任务调用）
     * @param expiredBeforeTime 过期时间点
     */
    public void cleanupExpiredTokens(long expiredBeforeTime) {
        try {
            // 清理用户token
            cleanupExpiredUserTokens(expiredBeforeTime);
            
            // 清理管理员token  
            cleanupExpiredAdminTokens(expiredBeforeTime);
            
        } catch (Exception e) {
            log.error("清理过期token失败", e);
        }
    }
    
    /**
     * 清理过期的用户token
     */
    private void cleanupExpiredUserTokens(long expiredBeforeTime) {
        String pattern = USER_TOKENS_PREFIX + "*";
        
        try {
            Set<String> keys = redisTemplate.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                for (String key : keys) {
                    // 删除过期的token (score < expiredBeforeTime)
                    Long removed = redisTemplate.opsForZSet()
                            .removeRangeByScore(key, 0, expiredBeforeTime);
                    
                    if (removed != null && removed > 0) {
                        log.debug("清理过期用户token - key: {}, removed: {}", key, removed);
                    }
                }
            }
            
        } catch (Exception e) {
            log.error("清理过期用户token失败", e);
        }
    }
    
    /**
     * 清理过期的管理员token
     */
    private void cleanupExpiredAdminTokens(long expiredBeforeTime) {
        String pattern = ADMIN_TOKENS_PREFIX + "*";
        
        try {
            Set<String> keys = redisTemplate.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                for (String key : keys) {
                    Long removed = redisTemplate.opsForZSet()
                            .removeRangeByScore(key, 0, expiredBeforeTime);
                    
                    if (removed != null && removed > 0) {
                        log.debug("清理过期管理员token - key: {}, removed: {}", key, removed);
                    }
                }
            }
            
        } catch (Exception e) {
            log.error("清理过期管理员token失败", e);
        }
    }
    
    /**
     * 获取用户当前活跃token数量
     * @param userId 用户ID
     * @return token数量
     */
    public long getUserActiveTokenCount(Long userId) {
        String key = USER_TOKENS_PREFIX + userId;
        
        try {
            Long count = redisTemplate.opsForZSet().zCard(key);
            return count != null ? count : 0;
            
        } catch (Exception e) {
            log.error("获取用户活跃token数量失败 - userId: {}", userId, e);
            return 0;
        }
    }
    
    /**
     * 强制下线用户的所有设备
     * @param userId 用户ID
     */
    public void forceLogoutAllUserDevices(Long userId) {
        String key = USER_TOKENS_PREFIX + userId;
        
        try {
            Long removed = redisTemplate.opsForZSet().removeRange(key, 0, -1);
            log.info("强制用户下线所有设备 - userId: {}, 移除token数量: {}", userId, removed);
            
        } catch (Exception e) {
            log.error("强制用户下线失败 - userId: {}", userId, e);
        }
    }
}
