package com.dora.util;

import com.dora.config.JwtConfig;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * JWT工具类
 */
@Slf4j
@Component
public class JwtUtil {

    private final JwtConfig jwtConfig;
    
    public JwtUtil(JwtConfig jwtConfig) {
        this.jwtConfig = jwtConfig;
    }
    
    // 移除硬编码的过期时间，改用配置文件中的设置

    /**
     * 获取JWT密钥
     */
    private SecretKey getSecretKey() {
        return Keys.hmacShaKeyFor(jwtConfig.getSecret().getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 生成JWT令牌（用户）
     * @param userId 用户ID
     * @param phone 手机号
     * @return JWT令牌
     */
    public String generateToken(Long userId, String phone) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("phone", phone); // phone可以为null，兼容未绑定手机号的用户
        claims.put("type", "user"); // 标识为用户token
        claims.put("jti", UUID.randomUUID().toString()); // 添加JWT ID
        
        return Jwts.builder()
                .claims(claims)
                .subject(phone != null ? phone : userId.toString()) // 没有手机号时使用userId作为subject
                .issuedAt(new Date())
                .expiration(new Date(System.currentTimeMillis() + jwtConfig.getExpiration()))
                .signWith(getSecretKey())
                .compact();
    }

    /**
     * 生成管理员JWT令牌
     * @param adminId 管理员ID
     * @param username 用户名
     * @param role 角色
     * @return JWT令牌
     */
    public String generateAdminToken(Long adminId, String username, Integer role) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("adminId", adminId);
        claims.put("username", username);
        claims.put("role", role);
        claims.put("type", "admin"); // 标识为管理员token
        claims.put("jti", UUID.randomUUID().toString()); // 添加JWT ID
        
        return Jwts.builder()
                .claims(claims)
                .subject(adminId.toString())
                .issuedAt(new Date())
                .expiration(new Date(System.currentTimeMillis() + jwtConfig.getExpiration()))
                .signWith(getSecretKey())
                .compact();
    }

    /**
     * 从令牌中获取用户ID
     * @param token JWT令牌
     * @return 用户ID
     */
    public Long getUserIdFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("userId", Long.class);
    }

    /**
     * 从令牌中获取管理员ID
     * @param token JWT令牌
     * @return 管理员ID
     */
    public Long getAdminIdFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("adminId", Long.class);
    }

    /**
     * 从令牌中获取手机号
     * @param token JWT令牌
     * @return 手机号
     */
    public String getPhoneFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("phone", String.class);
    }

    /**
     * 从令牌中获取用户名
     * @param token JWT令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("username", String.class);
    }

    /**
     * 从令牌中获取角色
     * @param token JWT令牌
     * @return 角色
     */
    public Integer getRoleFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("role", Integer.class);
    }

    /**
     * 从令牌中获取JWT ID
     * @param token JWT令牌
     * @return JWT ID
     */
    public String getJwtIdFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("jti", String.class);
    }

    /**
     * 从令牌中获取类型
     * @param token JWT令牌
     * @return 类型（user/admin）
     */
    public String getTypeFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.get("type", String.class);
    }

    /**
     * 检查是否为管理员token
     * @param token JWT令牌
     * @return 是否为管理员token
     */
    public boolean isAdminToken(String token) {
        try {
            String type = getTypeFromToken(token);
            return "admin".equals(type);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 检查是否为用户token
     * @param token JWT令牌
     * @return 是否为用户token
     */
    public boolean isUserToken(String token) {
        try {
            String type = getTypeFromToken(token);
            return "user".equals(type);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 验证令牌是否有效
     * @param token JWT令牌
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            return !claims.getExpiration().before(new Date());
        } catch (Exception e) {
            log.warn("JWT令牌验证失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 从令牌中获取Claims
     * @param token JWT令牌
     * @return Claims对象
     */
    private Claims getClaimsFromToken(String token) {
        return Jwts.parser()
                .verifyWith(getSecretKey())
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }

    /**
     * 获取令牌过期时间
     * @param token JWT令牌
     * @return 过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    /**
     * 检查令牌是否即将过期（24小时内）
     * @param token JWT令牌
     * @return 是否即将过期
     */
    public boolean isTokenExpiringSoon(String token) {
        try {
            Date expiration = getExpirationDateFromToken(token);
            long timeUntilExpiration = expiration.getTime() - System.currentTimeMillis();
            return timeUntilExpiration < 24 * 60 * 60 * 1000L; // 24小时
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 获取当前时间加上过期时长后的过期时间
     * @return 过期时间
     */
    public Date getTokenExpirationTime() {
        return new Date(System.currentTimeMillis() + jwtConfig.getExpiration());
    }

    /**
     * 获取过期时长（毫秒）
     * @return 过期时长
     */
    public long getExpirationTimeMillis() {
        return jwtConfig.getExpiration();
    }

    /**
     * 刷新用户token（如果需要的话）
     * 只有当token即将过期时才生成新token，否则返回原token
     * @param token 原始token
     * @return 刷新后的token信息
     */
    public RefreshTokenResult refreshUserToken(String token) {
        try {
            // 1. 验证token有效性
            if (!validateToken(token)) {
                return RefreshTokenResult.invalid("Token无效或已过期");
            }

            // 2. 检查是否为用户token
            if (!isUserToken(token)) {
                return RefreshTokenResult.invalid("非用户token，无法刷新");
            }

            // 3. 获取用户信息
            Long userId = getUserIdFromToken(token);
            String phone = getPhoneFromToken(token);
            
            // 只要求userId不为空，phone可以为空（兼容未绑定手机号的用户）
            if (userId == null) {
                return RefreshTokenResult.invalid("Token缺少用户ID信息");
            }

            // 4. 检查是否需要刷新（剩余时间少于1/3时刷新）
            Date expiration = getExpirationDateFromToken(token);
            long timeUntilExpiration = expiration.getTime() - System.currentTimeMillis();
            long refreshThreshold = jwtConfig.getExpiration() / 3; // 1/3 过期时间
            
            if (timeUntilExpiration > refreshThreshold) {
                // 不需要刷新，返回原token
                return RefreshTokenResult.noRefreshNeeded(token, expiration);
            }

            // 5. 生成新token（phone可能为null）
            String newToken = generateToken(userId, phone);
            Date newExpiration = getExpirationDateFromToken(newToken);
            
            log.debug("用户token刷新成功，用户ID: {}, 剩余时间: {}ms", userId, timeUntilExpiration);
            return RefreshTokenResult.refreshed(newToken, newExpiration);

        } catch (Exception e) {
            log.error("刷新用户token失败: {}", e.getMessage());
            return RefreshTokenResult.invalid("Token刷新失败: " + e.getMessage());
        }
    }

    /**
     * 刷新管理员token（如果需要的话）
     * @param token 原始token
     * @return 刷新后的token信息
     */
    public RefreshTokenResult refreshAdminToken(String token) {
        try {
            // 1. 验证token有效性
            if (!validateToken(token)) {
                return RefreshTokenResult.invalid("Token无效或已过期");
            }

            // 2. 检查是否为管理员token
            if (!isAdminToken(token)) {
                return RefreshTokenResult.invalid("非管理员token，无法刷新");
            }

            // 3. 获取管理员信息
            Long adminId = getAdminIdFromToken(token);
            String username = getUsernameFromToken(token);
            Integer role = getRoleFromToken(token);
            
            if (adminId == null || username == null || role == null) {
                return RefreshTokenResult.invalid("Token信息不完整");
            }

            // 4. 检查是否需要刷新
            Date expiration = getExpirationDateFromToken(token);
            long timeUntilExpiration = expiration.getTime() - System.currentTimeMillis();
            long refreshThreshold = jwtConfig.getExpiration() / 3; // 1/3 过期时间
            
            if (timeUntilExpiration > refreshThreshold) {
                // 不需要刷新，返回原token
                return RefreshTokenResult.noRefreshNeeded(token, expiration);
            }

            // 5. 生成新token
            String newToken = generateAdminToken(adminId, username, role);
            Date newExpiration = getExpirationDateFromToken(newToken);
            
            log.debug("管理员token刷新成功，管理员ID: {}, 剩余时间: {}ms", adminId, timeUntilExpiration);
            return RefreshTokenResult.refreshed(newToken, newExpiration);

        } catch (Exception e) {
            log.error("刷新管理员token失败: {}", e.getMessage());
            return RefreshTokenResult.invalid("Token刷新失败: " + e.getMessage());
        }
    }

    /**
     * Token刷新结果类
     */
    public static class RefreshTokenResult {
        private boolean success;
        private boolean refreshed;
        private String token;
        private Date expiration;
        private String message;

        private RefreshTokenResult(boolean success, boolean refreshed, String token, Date expiration, String message) {
            this.success = success;
            this.refreshed = refreshed;
            this.token = token;
            this.expiration = expiration;
            this.message = message;
        }

        public static RefreshTokenResult refreshed(String newToken, Date expiration) {
            return new RefreshTokenResult(true, true, newToken, expiration, "Token已刷新");
        }

        public static RefreshTokenResult noRefreshNeeded(String token, Date expiration) {
            return new RefreshTokenResult(true, false, token, expiration, "Token无需刷新");
        }

        public static RefreshTokenResult invalid(String message) {
            return new RefreshTokenResult(false, false, null, null, message);
        }

        // Getters
        public boolean isSuccess() { return success; }
        public boolean isRefreshed() { return refreshed; }
        public String getToken() { return token; }
        public Date getExpiration() { return expiration; }
        public String getMessage() { return message; }
    }
} 