package org.lingniu.sdk.service;

import lombok.extern.slf4j.Slf4j;
import org.lingniu.sdk.constant.CacheConstants;
import org.lingniu.sdk.model.token.AccessTokenInfo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RedisAccessTokenService {

    private final RedisTemplate<String, Object> redisTemplate;

    public RedisAccessTokenService(RedisTemplate<String, Object> sdkRedisTemplate) {
        this.redisTemplate = sdkRedisTemplate;
    }

    /**
     * 存储Access Token到Redis
     */
    public void storeAccessToken(AccessTokenInfo tokenInfo) {
        if (tokenInfo == null || tokenInfo.getTokenValue() == null) {
            log.error("tokenInfo或tokenValue为空");
            return;
        }

        String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, tokenInfo.getTokenValue());
        try {
            Map<String, Object> tokenMap = tokenInfo.toMap();

            // 使用Hash类型存储
            redisTemplate.opsForHash().putAll(key, tokenMap);

            // 设置过期时间
            Instant expiresAt = tokenInfo.getExpiresAt();
            // 1小时
            long expireSeconds = 3600;
            if (expiresAt != null) {
                expireSeconds = expiresAt.getEpochSecond() - Instant.now().getEpochSecond();
                if (expireSeconds <= 0) {
                    log.warn("token已过期，不再存储: {}", tokenInfo.getTokenValue());
                    redisTemplate.delete(key); // 删除可能已存在的key
                    return;
                }
            }

            redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS);
            log.debug("存储Access Token成功: key={}, expireSeconds={}", key, expireSeconds);

        } catch (Exception e) {
            log.error("存储Access Token失败: token={}", tokenInfo.getTokenValue(), e);
        }
    }

    /**
     * 验证Access Token
     */
    public boolean validateAccessToken(String token) {
        if (token == null || token.trim().isEmpty()) {
            return false;
        }

        String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token);

        // 检查key是否存在
        Boolean exists = redisTemplate.hasKey(key);
        if (!exists) {
            return false;
        }

        // 获取token信息并验证
        AccessTokenInfo accessTokenInfo = getAccessTokenInfo(token);
        return accessTokenInfo != null && accessTokenInfo.isValid();
    }

    /**
     * 删除Access Token
     */
    public boolean removeAccessToken(String token) {
        if (token == null || token.trim().isEmpty()) {
            return false;
        }

        String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token);
        try {
            Boolean deleted = redisTemplate.delete(key);
            log.debug("删除Access Token: key={}, 结果={}", key, deleted);
            return deleted;
        } catch (Exception e) {
            log.error("删除Access Token失败: token={}", token, e);
            return false;
        }
    }

    /**
     * 获取Access Token信息
     */
    public AccessTokenInfo getAccessTokenInfo(String token) {
        if (token == null || token.trim().isEmpty()) {
            return null;
        }

        String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token);
        try {
            // 获取所有hash字段
            Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
            if (entries.isEmpty()) {
                return null;
            }

            // 转换为Map<String, Object>
            Map<String, Object> cacheMap = convertMap(entries);
            return AccessTokenInfo.fromMap(cacheMap);

        } catch (Exception e) {
            log.error("获取Access Token信息失败: token={}", token, e);
            return null;
        }
    }

    /**
     * 作废 删除
     */
    public void revokeAccessToken(AccessTokenInfo tokenInfo) {
        if (tokenInfo == null || tokenInfo.getTokenValue() == null) {
            return;
        }

        String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, tokenInfo.getTokenValue());
        try {
            Boolean deleted = redisTemplate.delete(key);
            log.debug("作废Access Token: key={}, 结果={}", key, deleted);
        } catch (Exception e) {
            log.error("作废Access Token失败: token={}", tokenInfo.getTokenValue(), e);
        }
    }

    /**
     * 刷新Access Token过期时间
     */
    public boolean refreshTokenExpire(String token, long expireSeconds) {
        if (token == null || token.trim().isEmpty() || expireSeconds <= 0) {
            return false;
        }

        String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token);
        try {
            Boolean exists = redisTemplate.hasKey(key);
            if (exists) {
                Boolean refreshed = redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS);
                log.debug("刷新Token过期时间: key={}, expireSeconds={}, 结果={}",
                        key, expireSeconds, refreshed);
                return refreshed;
            }
            return false;
        } catch (Exception e) {
            log.error("刷新Token过期时间失败: token={}", token, e);
            return false;
        }
    }

    /**
     * 获取Token剩余生存时间
     */
    public Long getTokenTtl(String token) {
        if (token == null || token.trim().isEmpty()) {
            return null;
        }

        String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token);
        try {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("获取Token剩余生存时间失败: token={}", token, e);
            return null;
        }
    }

    /**
     * 辅助方法：转换Map类型
     */
    @SuppressWarnings("unchecked")
    private Map<String, Object> convertMap(Map<Object, Object> originalMap) {
        // 根据实际情况转换，这里假设键值都是可序列化的对象
        // 如果键都是String，值都是可序列化的对象，可以直接转换
        return (Map<String, Object>) (Map<?, ?>) originalMap;
    }

    /**
     * 另一种实现方式：使用StringRedisTemplate（如果需要明确的字符串类型）
     *
     * @Autowired
     * private StringRedisTemplate stringRedisTemplate;
     *
     * 注意：使用StringRedisTemplate时，存储和获取Map需要额外的序列化处理
     */

    /**
     * 批量删除包含特定模式的token（可选功能）
     */
    public Long batchRemoveTokens(String pattern) {
        if (pattern == null || pattern.trim().isEmpty()) {
            return 0L;
        }

        try {
            // 使用scan命令避免阻塞
            var keys = redisTemplate.keys(pattern);
            if (!keys.isEmpty()) {
                Long count = redisTemplate.delete(keys);
                log.debug("批量删除Token: pattern={}, 删除数量={}", pattern, count);
                return count;
            }
            return 0L;
        } catch (Exception e) {
            log.error("批量删除Token失败: pattern={}", pattern, e);
            return 0L;
        }
    }
}