package cn.aqroid.business.auth.service.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson2.JSON;

import cn.aqroid.business.auth.config.WxConfig;
import cn.aqroid.business.auth.domain.ApiLoginUser;
import cn.aqroid.business.auth.service.ApiTokenService;
import cn.aqroid.common.utils.StringUtils;
import cn.aqroid.common.utils.uuid.IdUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * API Token服务实现
 *
 * @author aqroid
 * @date 2026-01-12
 */
@Service
public class ApiTokenServiceImpl implements ApiTokenService {

    private static final Logger log = LoggerFactory.getLogger(ApiTokenServiceImpl.class);

    /** Token前缀 */
    private static final String TOKEN_PREFIX = "Bearer ";

    /** Redis缓存key前缀 */
    private static final String API_LOGIN_USER_KEY = "api_login_user:";

    /** JWT Claims中的用户ID */
    private static final String CLAIM_USER_ID = "user_id";

    /** JWT Claims中的openid */
    private static final String CLAIM_OPENID = "openid";

    /** JWT Claims中的用户类型 */
    private static final String CLAIM_USER_TYPE = "user_type";

    /** JWT Claims中的租户ID */
    private static final String CLAIM_TENANT_ID = "tenant_id";

    /** JWT Claims中的Token UUID */
    private static final String CLAIM_TOKEN_UUID = "token_uuid";

    @Value("${token.secret}")
    private String secret;

    @Autowired
    private WxConfig wxConfig;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 创建Token
     */
    @Override
    public String createToken(ApiLoginUser loginUser) {
        String tokenUuid = IdUtils.fastUUID();
        loginUser.setToken(tokenUuid);
        
        long currentTime = System.currentTimeMillis();
        loginUser.setLoginTime(currentTime);
        loginUser.setExpireTime(currentTime + wxConfig.getTokenExpireTime() * 1000);

        // 将用户信息存入Redis
        String userKey = API_LOGIN_USER_KEY + tokenUuid;
        redisTemplate.opsForValue().set(userKey, JSON.toJSONString(loginUser), 
                wxConfig.getTokenExpireTime(), TimeUnit.SECONDS);

        // 生成JWT Token
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_TOKEN_UUID, tokenUuid);
        claims.put(CLAIM_USER_ID, loginUser.getUserId());
        claims.put(CLAIM_OPENID, loginUser.getOpenid());
        claims.put(CLAIM_USER_TYPE, loginUser.getUserType());
        claims.put(CLAIM_TENANT_ID, loginUser.getTenantId());

        return Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 验证Token并获取用户信息
     */
    @Override
    public ApiLoginUser validateToken(String token) {
        if (StringUtils.isEmpty(token)) {
            return null;
        }

        try {
            Claims claims = parseToken(token);
            if (claims == null) {
                return null;
            }

            String tokenUuid = (String) claims.get(CLAIM_TOKEN_UUID);
            if (StringUtils.isEmpty(tokenUuid)) {
                return null;
            }

            // 从Redis获取用户信息
            String userKey = API_LOGIN_USER_KEY + tokenUuid;
            String userJson = redisTemplate.opsForValue().get(userKey);
            
            if (StringUtils.isEmpty(userJson)) {
                log.debug("Token已过期或不存在: {}", tokenUuid);
                return null;
            }

            ApiLoginUser loginUser = JSON.parseObject(userJson, ApiLoginUser.class);
            
            // 检查是否需要刷新Token
            long currentTime = System.currentTimeMillis();
            long expireTime = loginUser.getExpireTime();
            long refreshThreshold = wxConfig.getTokenExpireTime() * 1000 / 3; // 剩余1/3时间时刷新
            
            if (expireTime - currentTime < refreshThreshold) {
                refreshToken(loginUser);
            }

            return loginUser;
        } catch (Exception e) {
            log.error("验证Token异常", e);
            return null;
        }
    }

    /**
     * 刷新Token
     */
    @Override
    public void refreshToken(ApiLoginUser loginUser) {
        long currentTime = System.currentTimeMillis();
        loginUser.setExpireTime(currentTime + wxConfig.getTokenExpireTime() * 1000);

        String userKey = API_LOGIN_USER_KEY + loginUser.getToken();
        redisTemplate.opsForValue().set(userKey, JSON.toJSONString(loginUser), 
                wxConfig.getTokenExpireTime(), TimeUnit.SECONDS);
    }

    /**
     * 使Token失效
     */
    @Override
    public void invalidateToken(String token) {
        if (StringUtils.isEmpty(token)) {
            return;
        }

        try {
            Claims claims = parseToken(token);
            if (claims != null) {
                String tokenUuid = (String) claims.get(CLAIM_TOKEN_UUID);
                if (StringUtils.isNotEmpty(tokenUuid)) {
                    String userKey = API_LOGIN_USER_KEY + tokenUuid;
                    redisTemplate.delete(userKey);
                }
            }
        } catch (Exception e) {
            log.error("使Token失效异常", e);
        }
    }

    /**
     * 从请求头中获取Token
     */
    @Override
    public String getTokenFromHeader(String header) {
        if (StringUtils.isNotEmpty(header) && header.startsWith(TOKEN_PREFIX)) {
            return header.substring(TOKEN_PREFIX.length());
        }
        return header;
    }

    /**
     * 获取Token的唯一标识
     */
    @Override
    public String getTokenKey(String token) {
        try {
            Claims claims = parseToken(token);
            if (claims != null) {
                return (String) claims.get(CLAIM_TOKEN_UUID);
            }
        } catch (Exception e) {
            log.error("获取Token Key异常", e);
        }
        return null;
    }

    /**
     * 解析JWT Token
     */
    private Claims parseToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.debug("解析Token失败: {}", e.getMessage());
            return null;
        }
    }
}
